MySQL、PostgreSQL子查询必须加别名如AS t否则报错CTE更安全适用于多次复用汇总结果HAVING仅过滤分组后结果二次加工需在外层性能敏感时慎用嵌套子查询替代JOIN。子查询嵌套时别忘了给内层结果起别名MySQL、PostgreSQL 都要求子查询必须有别名否则直接报错 Every derived table must have its own alias。这不是语法糖是解析器强制要求——因为外层要通过别名引用内层字段。常见错误是写完 SELECT * FROM (SELECT user_id, COUNT(*) AS cnt FROM orders GROUP BY user_id) 就停了漏掉 AS t 这部分。必须加 AS 或空格后跟别名比如 (SELECT ... ) AS order_summary别名不能和外层表重名否则可能引发字段歧义尤其在 JOIN 场景下子查询里用 ORDER BY 没意义除非配合 LIMIT排序由外层控制CTE 中多次引用同一汇总结果更安全当你要对分组结果做多个不同方向的二次加工比如既算 Top 5又算平均值再筛出高于均值的记录CTE 比重复写子查询更可靠避免逻辑不一致、减少重复计算、提升可读性。注意 CTE 不是视图它只在当前语句生命周期内有效而且 PostgreSQL 支持递归 CTEMySQL 8.0 才支持非递归 CTE。CTE 定义后必须紧跟一个主查询不能中间插 INSERT 或 UPDATE别名里不能有横线或空格user_stats_v1 可以user-stats 或 user stats 会报错CTE 内部不能引用自身除非显式声明为 RECURSIVEWITH order_counts AS ( SELECT user_id, COUNT(*) AS cnt FROM orders GROUP BY user_id)SELECT user_id, cntFROM order_countsWHERE cnt (SELECT AVG(cnt) FROM order_counts);HAVING 和 WHERE 混用导致过滤失效很多人想“先按用户汇总再筛出订单数 10 的人最后算这些人平均下单金额”结果把 HAVING cnt 10 和外层条件混在一起发现数据不对——本质是搞错了过滤层级。HAVING 只能过滤分组后的结果而二次加工比如算均值、排名、关联其他表必须在外层完成。一旦在子查询或 CTE 里漏掉关键分组字段外层就无法正确关联或聚合。子查询中如果只 SELECT COUNT(*) 没选 user_id外层就无法知道这个计数属于谁HAVING 不能引用外层字段也不能用窗口函数别名如 HAVING rank_num 1 是错的需要排序取 Top N子查询里用 ORDER BY ... LIMIT别指望 HAVING 干这事性能敏感场景慎用嵌套子查询替代 JOIN对大表做分组汇总后再 JOIN 其他表用子查询有时比直接 JOIN GROUP BY 更慢——因为优化器难推导中间结果集大小可能放弃使用索引。特别是 MySQL 5.7 及更早版本子查询常被物化为临时表且无索引而 CTE 在 PostgreSQL 中默认不物化除非被多次引用性能更可控。确认执行计划是否出现 Using temporary; Using filesort这是危险信号如果外层只是单字段过滤如 WHERE cnt 5优先考虑 GROUP BY ... HAVING 一步到位涉及多表关联时先在子查询/CTE 中完成所有 JOIN 和 WHERE再 GROUP BY别反过来CTE 和子查询不是二选一而是看二次加工是否复用、是否需递归、以及目标数据库版本是否支持——低版本 MySQL 用户基本只能靠子查询加别名硬扛。