where 先执行,having 后执行;where 在 group by 前过滤行,只能用原始字段;having 在分组后过滤组,必须配合 group by,且只能用聚合函数或 group by 列。

WHERE 和 HAVING 混用时到底谁先执行?
WHERE 过滤行,HAVING 过滤分组结果,顺序不能颠倒。写反了会报错或逻辑出错,比如 HAVING 里用了未聚合的列,数据库直接拒绝执行。
-
WHERE在GROUP BY前执行,只能引用原始表字段;HAVING在分组后执行,必须配合GROUP BY,且只能用聚合函数或GROUP BY列 - 想筛“订单金额大于 100 的用户”,用
WHERE order_amount > 100;想筛“平均订单额超 500 的用户”,必须用HAVING AVG(order_amount) > 500 - mysql 5.7+ 默认开启
sql_mode=only_full_group_by,如果select列没在GROUP BY中又没被聚合,会报错:Expression #1 of SELECT list is not in GROUP BY clause
窗口函数 ROW_NUMBER() vs RANK() vs DENSE_RANK()
三者都生成排序编号,但对并列值的处理完全不同,选错会导致业务指标偏差——比如排行榜去重、Top N 统计、分页跳过重复排名等场景极易踩坑。
-
ROW_NUMBER()严格按顺序编号,相同值也不同号:1,2,2,4 -
RANK()并列则同号,跳过后续编号:1,2,2,4 -
DENSE_RANK()并列同号,不跳号:1,2,2,3 - 分页取“前 10 名”用
ROW_NUMBER()最稳妥;做“进入前 3 名的用户列表”得用DENSE_RANK(),否则第 4 名可能实际是并列第 3
JOIN 多表时 NULL 值怎么安全处理?
LEFT JOIN 后字段为 NULL 是常态,但直接参与计算或比较会静默失败——比如 WHERE status = 'active' 会自动过滤掉所有 NULL 行,而你可能想保留左表记录。
- 判断是否为
NULL必须用IS NULL或IS NOT NULL,不能用= NULL(永远返回UNKNOWN) - 聚合前补
COALESCE(status, 'unknown')避免GROUP BY分裂;数值计算前用COALESCE(amount, 0)防止整个表达式变NULL - postgresql 支持
IS DISTINCT FROM安全比较,MySQL 和 SQL Server 得靠COALESCE(a, '') = COALESCE(b, '')
EXPLAIN 看不出性能问题?重点盯这三列
EXPLAIN 输出里真正决定慢不慢的不是 type 或 rows,而是 key、Extra 和 filtered —— 尤其当 key 是 NULL 或 Extra 出现 using filesort、Using temporary 时,基本等于没走索引。
-
key为NULL:没命中任何索引,强制全表扫描 -
Extra含Using index condition是好信号(ICP 优化启用);含Using where; Using index表示覆盖索引,最快 -
filtered值太低(比如10.00)说明 WHERE 条件过滤性差,即使走了索引也要读大量无效行
实际写复杂分析查询时,最容易被忽略的是字段别名在 GROUP BY 或 HAVING 中的可用性——标准 SQL 不允许,MySQL 旧版本纵容但新版本默认报错,别依赖它。