sql执行顺序决定作用域:where在group by前执行,无法使用avg()等聚合函数;过滤分组结果须用having;子查询不能引用外层group by字段;关联列仅限where中使用;null需用coalesce兜底。

因为聚合结果在子查询执行时尚未生成,SQL 执行顺序决定了作用域不可逆
WHERE 里写 AVG() 或 COUNT() 会直接报错
比如 SELECT * FROM emp WHERE salary > (SELECT AVG(salary) FROM emp) 是合法的,但 SELECT dept_id, AVG(salary) FROM emp GROUP BY dept_id WHERE AVG(salary) > (SELECT AVG(salary) FROM emp) 一定失败——WHERE 阶段根本看不到 GROUP BY 后的 AVG(salary),它连分组都还没做。
- SQL 标准执行顺序是:
FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY -
WHERE运行时,数据还是原始行,没分组、没聚合,所有聚合函数都“不存在” - 想过滤分组后结果,必须用
HAVING,不是WHERE
子查询不能引用外层 GROUP BY 字段
常见错误写法:SELECT dept_id, (SELECT COUNT(*) FROM emp e2 WHERE e2.dept_id = emp.dept_id) cnt FROM emp GROUP BY dept_id。表面看是关联子查询,但问题在于:外层已 GROUP BY dept_id,而子查询里 emp.dept_id 在语义上已不是单值(它是分组键,不是具体某行的值),部分数据库(如 PostgreSQL)会直接拒绝。
- 正确做法是把聚合逻辑收进子查询内部,显式关联:
SELECT d.dept_id, coalesce(t.cnt, 0) FROM dept d LEFT JOIN (SELECT dept_id, COUNT(*) cnt FROM emp GROUP BY dept_id) t ON t.dept_id = d.dept_id - 别依赖“看起来能推断”的字段关系,
WHERE中的关联条件必须是明确的列名对列名,且两边都处于同一作用域 - 相关子查询性能极差,10 万部门 × 每次查员工表 = 全表扫描百万次,大表务必避免
嵌套太深时,GROUP BY 字段在子子查询里彻底失效
三层嵌套中试图在最内层用 outer_table.id 做过滤,大概率报 Unknown column。SQL 不支持跳层引用,AnalyticDB、MySQL 8.0+ 等多数引擎都会拒绝解析。
- 关联列只允许出现在子查询的
WHERE子句中,不能出现在SELECT、FROM或更深层子查询里 - 例如:
SELECT id, (SELECT SUM(v) FROM t2 WHERE t2.ref = t1.id) FROM t1可行;但(SELECT SUM(v) + t1.id FROM t2 WHERE ...)就非法 - 真要复用聚合值,优先用
WITH提前算好,比多层子查询清晰、可控、易索引
最容易被忽略的一点:聚合值是否为 NULL 往往决定整个条件是否生效。子查询返回 NULL 时,> NULL 或 = NULL 全部为 UNKNOWN,整行被过滤掉——记得用 COALESCE 或 IS NOT NULL 显式兜底。










