子查询不能直接引用外层GROUP BY字段,需用WHERE显式关联;相关子查询性能差、可读性低,应优先用CTE或窗口函数替代;注意NULL处理、排序LIMIT不确定性及层级计算定位。
子查询里不能直接用外层 GROUP BY 字段
很多人写分层聚合时,想在子查询里直接引用外层的
或
,结果报错或返回空。这是因为子查询默认无法访问外层查询的非聚合字段(除非是相关子查询且明确关联)。
正确做法:用
显式关联,比如子查询加
错误写法:
—— 这里的
会解析成子查询自己的列(若存在),不是外层的
相关子查询性能差:每行外层记录都会触发一次子查询,10万行用户 × 每次查千条订单 = 百万级扫描,别在大表上这么干
多层嵌套子查询导致可读性崩坏和优化器失效
三层以上
看似能算出“每个用户最近3单的平均折扣率”,但实际执行计划里,优化器常放弃重写为 JOIN,甚至跳过索引。
替代方案:优先用
(CTE)拆解,比如先
MySQL 5.7 不支持 CTE?那就用临时表或派生表,但注意
有会话生命周期限制
特别警惕:嵌套子查询中用了
,在无
的上下文里可能返回任意一行,不是“最新”
聚合函数 + 子查询混用时 NULL 处理不一致
、
对空集合返回
,但子查询单独执行返回空集时,整个表达式可能变成
或报错(取决于数据库),而你未必意识到这是数据缺失导致的。
显式兜底:用
,别依赖默认行为
注意
在子查询中遇到全
列会返回
,不是 0;而
永远返回数字,适合做存在性判断
PostgreSQL 中
常被误用——它只是把 0 变成 NULL,不影响分母计数,结果仍可能偏高
窗口函数其实比子查询更适合分层聚合场景
想算“每个品类下销售额前3的商品”,用子查询套
会因 MySQL 版本或严格模式报错;用
一行解决,且可复用排序逻辑。
兼容性提醒:MySQL 8.0+、PostgreSQL 8.4+、SQL Server 2005+ 支持标准窗口函数;SQLite 3.25+ 也支持,但旧版 HiveQL 需用
UDAF
性能更稳:窗口函数通常只扫描一次主表,子查询可能重复扫描
注意陷阱:
和
对并列排名处理不同,选错会导致“前3”实际返回 5 行
复杂聚合真正卡住人的,往往不是语法写不对,而是没想清楚「这层计算该在哪一层完成」——是在原始明细层过滤,还是在中间聚合层加工,还是留到应用层补算。子查询容易让人在层级间反复横跳,最后连自己都看不懂执行路径。
user_idorder_dateWHEREWHERE t2.user_id = t1.user_idSELECT (SELECT SUM(amount) FROM orders WHERE user_id = user_id) ...user_idSELECT (SELECT (SELECT ...))WITHWITH recent_orders AS (...) SELECT ... FROM recent_orders GROUP BY ...CREATE TEMPORARY TABLEORDER BY ... LIMIT 1GROUP BYSUM()AVG()NULLNULLCOALESCE((SELECT SUM(...) ...), 0)AVG()NULLNULLCOUNT(*)SELECT AVG(NULLIF(x, 0))LIMITROW_NUMBER() OVER (PARTITION BY category ORDER BY sales DESC)rank()RANK()DENSE_RANK()