GROUP BY 中不能直接使用 REGEXP 等正则函数,因其非确定性违反 SQL 分组要求;必须用 CASE WHEN 将正则判断结果映射为离散标签(如 'gmail'/'microsoft'),再对标签分组,并在 GROUP BY 中重复该 CASE 表达式以确保兼容性和准确性。

GROUP BY 里不能直接用 REGEXP 或类似正则函数
几乎所有主流数据库(MySQL、PostgreSQL、SQL Server)都不允许在 GROUP BY 子句中直接写 REGEXP、~、REGEXP_LIKE 等正则表达式——不是语法不支持,而是 SQL 标准要求分组字段必须是“确定性表达式”,而多数正则函数在某些场景下(如带标志位、跨 locale)可能产生非确定性行为,数据库引擎会拒绝执行。
常见错误现象:ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause 或直接报 “non-deterministic function in GROUP BY”。
- MySQL 8.0+ 允许
REGEXP出现在SELECT中,但GROUP BY仍需显式复写该表达式(不能用别名) - PostgreSQL 的
~和~*同样不能直接用于GROUP BY,除非包裹在IMMUTABLE函数中(极少用) - Oracle 的
REGEXP_LIKE是布尔函数,返回 true/false,无法直接分组;得用CASE WHEN REGEXP_LIKE(...) THEN 'A' ELSE 'B' END
用 CASE WHEN + 正则判断实现可分组的分类标签
真正能落地的做法,是把正则逻辑“翻译”成明确的字符串标签,再对这个标签分组。本质是用正则做条件判断,输出离散类别值。
例如:从 email 字段提取域名类型(gmail.com / outlook.com / 其他),并统计各类数量:
SELECT
CASE
WHEN email REGEXP '@gmail\.com$' THEN 'gmail'
WHEN email REGEXP '@(outlook|hotmail)\.com$' THEN 'microsoft'
WHEN email REGEXP '@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' THEN 'other_domain'
ELSE 'invalid'
END AS domain_type,
COUNT(*) AS cnt
FROM users
GROUP BY
CASE
WHEN email REGEXP '@gmail\.com$' THEN 'gmail'
WHEN email REGEXP '@(outlook|hotmail)\.com$' THEN 'microsoft'
WHEN email REGEXP '@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' THEN 'other_domain'
ELSE 'invalid'
END;
注意点:
-
REGEXP中的点号.必须转义为\.(MySQL)或\./[.](PostgreSQL),否则匹配任意字符 - 结尾锚定用
$,避免@gmail.com.xyz被误判 - 必须写
ELSE,否则NULL邮箱或格式异常的行全归入同一组,导致漏统计 - MySQL 8.0+ 支持在
GROUP BY引用SELECT中的别名,但为兼容旧版本或跨库迁移,建议始终重复写表达式
性能敏感时,避免在 GROUP BY 中实时计算正则
正则匹配本身开销不小,如果表有百万行,每次分组都要对每行执行多轮模式匹配,很容易拖慢查询。尤其当正则较复杂(如嵌套量词、回溯多)时,CPU 使用率会明显升高。
优化方向:
- 提前物化分类结果:加一个生成列(MySQL)或计算列(SQL Server),例如
domain_class VARCHAR(20) AS (CASE WHEN ... END) STORED,再对该列建索引 - 用更轻量的字符串函数替代部分正则:比如提取域名可用
SUBSTRING_INDEX(email, '@', -1)(MySQL)或SPLIT_PART(email, '@', 2)(PostgreSQL),再用=或IN判断,比正则快一个数量级 - 若只需区分有限几种模式(如前缀/后缀),优先用
LIKE:email LIKE '%@gmail.com'比REGEXP '@gmail\.com$'更快,且能走前缀索引(如果字段有索引)
不同数据库对正则的支持和写法差异
别指望一条 SQL 通吃所有数据库。关键差异点:
- MySQL:用
REGEXP或RLIKE,区分大小写取决于列 collation;REGEXP_SUBSTR可提取子串,但不能直接用于分组 - PostgreSQL:用
~(区分大小写)或~*(不区分),模式写法类似 PCRE,但不支持d等简写,得写[0-9] - SQL Server:无原生正则,得用
LIKE拼凑,或启用 CLR 函数,或升级到 2022+ 用STRING_SPLIT+ 多层CASE模拟 - BigQuery / Snowflake:支持
REGEXP_CONTAINS和REGEXP_EXTRACT,后者可直接产出分组字段,GROUP BY可引用别名(Snowflake 支持,BigQuery 需重复)
最易被忽略的是转义和锚定细节——少一个 \ 或漏掉 $,就可能把测试数据跑通、线上大批数据错分。上线前务必用真实脏数据验证分类逻辑。

















