group by后的条件统计必须将case when嵌入count或sum等聚合函数内,这是唯一安全标准做法;写在where或having中会因作用域错误导致报错或逻辑混乱。

GROUP BY 后的条件统计必须用 CASE WHEN 套在聚合函数里
直接在 SELECT 中对每行做判断,再交给 COUNT 或 SUM 聚合,是唯一安全且标准的做法。写到 WHERE 或 HAVING 里会报错或逻辑错乱——因为那俩只管“要不要这行”或“要不要这组”,不负责“这组里怎么算分支”。
常见错误现象:COUNT(CASE WHEN salary > 15000 THEN 1 END) 得到 NULL 或意外 0;用 SUM(CASE WHEN ... THEN 1) 却没写 ELSE 0,导致整列结果为 NULL。
-
COUNT自动忽略NULL,所以COUNT(CASE WHEN cond THEN 1 END)可以省略ELSE,但前提是明确接受“不匹配即跳过”语义 -
SUM遇到NULL会传播NULL,必须写成SUM(CASE WHEN cond THEN 1 ELSE 0 END) - 所有
CASE WHEN分支都基于原始表的单行数据,不能引用聚合结果(如AVG(salary))
HAVING 不能直接写 CASE WHEN 判断分组结果
HAVING 只接受聚合函数输出或分组字段,不支持表达式分支。想对分组后的统计值打标签(比如“高均薪部门”“低活跃度品类”),得先产出分组结果,再在外层套 CASE WHEN。
错误写法:HAVING AVG(salary) > (CASE WHEN dept = 'tech' THEN 12000 ELSE 10000 END) —— 大多数数据库直接报错或行为未定义。
- 正确做法:用 CTE 或子查询先算出
dept和avg_salary,再对外层字段做CASE WHEN avg_salary > 12000 THEN 'A' ELSE 'B' -
HAVING里允许AVG(salary) > 12000,但不允许CASE、COALESCE、函数调用等任意表达式 - MySQL 和 PostgreSQL 对
HAVING表达式限制一致,别指望某一个能通融
跨组比较必须靠窗口函数 + 外层 CASE WHEN
如果要判断“本部门均薪是否高于公司均值”,不能在 GROUP BY 子句里写窗口函数,也不能把 AVG() OVER() 直接塞进 CASE WHEN 里和分组字段混用——语法不允许多重上下文嵌套。
典型陷阱:在 GROUP BY dept 的主查询里直接写 CASE WHEN AVG(salary) > AVG(salary) OVER() THEN 'above'...,会触发错误“invalid use of aggregate function”。
- 必须用 CTE 先完成分组聚合,再在 CTE 输出集上计算窗口值,最后用外层
SELECT做判断 -
AVG(salary) OVER()是对 CTE 结果集(已按 dept 分好组的行)计算全局均值,不是对原始表算——否则会因非分组字段冲突报错 - PostgreSQL 和 MySQL 8.0+ 在此流程上行为一致,但 SQLite 不支持窗口函数,需改用子查询模拟
位运算聚合(BIT_AND/BIT_OR)不是通用逻辑判断替代方案
BIT_AND 和 BIT_OR 是位级归约,不是布尔逻辑判断。它只在 MySQL 中原生支持,且要求字段为整型;PostgreSQL 需手动转换,SQL Server 和 Oracle 几乎不可用。
误用表现:对字符串字段 status 直接套 BIT_AND(status),MySQL 静默转成 0,结果全为 0;或期望 BIT_AND(1,1,0) 返回 “真值多数”,实际得 0。
- 真正适用场景:权限掩码(如
status & 4 = 4表示“全员有编辑权”)、状态位交并判断 - 更可读、更兼容的做法是提前用
CASE WHEN status & 4 = 4 THEN 1 ELSE 0解包成布尔,再用MIN/MAX聚合 - 别指望索引能加速
BIT_AND后的位判断,执行计划必走全表分组










