GROUP BY 中不能直接使用 REGEXP 等正则函数,因其非确定性;应改用 CASE WHEN 结合正则生成离散标签后再分组,并注意跨库语法差异与转义、锚定、ELSE 分支等细节。

GROUP BY 里不能直接用 REGEXP 或 ~
几乎所有主流数据库(MySQL、PostgreSQL、SQL Server、Oracle)都不允许在 GROUP BY 子句中直接写正则表达式函数。这不是语法不支持,而是 SQL 标准要求分组字段必须是“确定性表达式”,而正则函数在 locale、标志位(如大小写敏感)、多字节字符等场景下可能产生非确定性行为,引擎会拒绝执行。
常见错误现象包括:
ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause- PostgreSQL 报
non-deterministic function in GROUP BY - Oracle 报
ORA-30483: window functions are not allowed here(当误用于分析函数上下文)
根本原因:正则函数返回布尔值或匹配结果,不是可用于分组的离散标签。
CASE WHEN + 正则判断才是可分组的分类方式
真正能落地的做法,是把正则逻辑“翻译”成明确的字符串标签(如 'gmail'、'microsoft'、'invalid'),再对这个标签分组。本质是用正则做条件判断,输出离散类别值。
例如统计邮箱域名类型:
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),否则匹配任意字符 - 结尾必须用
$锚定,避免foo@gmail.com.xyz被误判为 gmail - 必须写
ELSE分支,否则NULL或格式异常的行全归入同一组,导致漏统计 - MySQL 8.0+ 允许在
GROUP BY引用SELECT中的别名,但为兼容旧版本或跨库迁移,建议始终重复写完整CASE表达式
条件聚合时用 CASE WHEN 套正则更安全
如果只是想按正则结果做条件计数或求和(不需要分组),可以直接在聚合函数内嵌套 CASE WHEN + 正则判断,无需担心分组限制。
比如统计含特定关键词的评论数:
SELECT SUM(CASE WHEN comment REGEXP 'error|fail|timeout' THEN 1 ELSE 0 END) AS error_cnt, SUM(CASE WHEN comment REGEXP 'success|ok|done' THEN 1 ELSE 0 END) AS success_cnt FROM logs;
这种写法比子查询高效,也比多个 WHERE + UNION ALL 更简洁。但要注意:
- PostgreSQL 用
~或~*替代REGEXP,且模式需加单引号,如comment ~* 'error|fail' - Oracle 没有
REGEXP直接返回布尔值的函数,得用REGEXP_COUNT(comment, 'pattern') > 0判断 - 性能上,正则匹配比普通
LIKE慢,大数据量时建议先加索引字段(如提取域名后单独建列)
容易被忽略的跨库兼容细节
不同数据库对正则的支持差异不小,硬写一个通用语句几乎不可能。最容易踩的坑是:
- MySQL 的
REGEXP默认区分大小写;PostgreSQL 的~区分,~*不区分;Oracle 的REGEXP_LIKE需显式加'i'标志 - MySQL 8.0+ 支持
REGEXP_LIKE()函数,返回布尔值,但依然不能进GROUP BY - SQL Server 没有原生正则,得用
LIKE模拟或 CLR 函数,LIKE不支持分组逻辑的灵活映射 - 所有数据库都要求正则模式是常量字符串,不能是字段拼接(如
email REGEXP CONCAT('@', domain_col, '$')在多数引擎中非法)
所以最稳妥的方式,不是追求“一行正则打天下”,而是提前把正则分类结果固化为计算列或物化视图,让聚合层只面对确定性字段。











