执行计划选MERGE JOIN是因为优化器基于统计信息判断两表连接字段天然有序或排序代价更低;聚合后结果集虽小但顺序丢失,且统计信息不准会导致误选并引发性能暴跌。

为什么执行计划里突然选了 MERGE JOIN?
不是你写了 MERGE 语句,而是优化器在执行 JOIN 时选了 MERGE JOIN 算法——它和 MERGE 语句无关,只是名字撞车了。真正触发它的,是统计信息反映出的两个表连接字段已“天然有序”或经排序后代价更低。比如:两表都按 user_id 建了聚簇索引(SQL Server)或主键(PostgreSQL),且查询中没破坏该顺序(如未在 ON 条件外加 ORDER BY RAND()),优化器就倾向选 MERGE JOIN。
聚合后结果集小,为什么反而可能避开 MERGE JOIN?
聚合(GROUP BY)会大幅压缩行数,但也会“抹平”原始顺序。例如:orders 表按 order_id 有序,但 SELECT user_id, SUM(amount) FROM orders GROUP BY user_id 的输出不再保证 user_id 有序(除非显式加 ORDER BY user_id)。此时若再 JOIN users 表,优化器发现:左表无序、右表虽有序,但无法直接归并,于是转向 HASH JOIN 或 NESTED LOOP。
- 聚合本身不保留输入顺序,除非加
ORDER BY——但排序有开销,得权衡 -
MERGE JOIN要求双方至少一方已排序,且排序键匹配ON字段 - 若聚合后结果集只有几百行,
HASH JOIN构建哈希表更快,优化器会主动跳过MERGE JOIN
统计信息不准,MERGE JOIN 就会选错
优化器依赖统计信息估算“排序成本”。如果 ANALYZE(PostgreSQL)或 UPDATE STATISTICS(SQL Server)没更新,它可能误判:以为某列高度有序(直方图梯级分布均匀),实际数据严重倾斜(90% 值为 NULL 或重复值扎堆),导致强行走 MERGE JOIN 却在运行时反复回退、重排,性能暴跌。
- 检查直方图:用
DBCC SHOW_STATISTICS(SQL Server)或\d+ table_name(PostgreSQL)看第一列分布 - 重点看
distinct_range_rows和range_rows是否远低于真实值 - 对聚合字段(如
user_id)单独建统计信息,比依赖全表抽样更准
先聚合再 JOIN,统计信息该怎么更新?
别只更新原表。聚合结果本质是新逻辑表,但数据库不自动为其生成统计信息。如果你常跑 WITH order_summary AS (SELECT user_id, SUM(amount) ...),就得手动干预:
- 把聚合结果物化为临时表,再对其执行
ANALYZE(PostgreSQL)或CREATE STATISTICS(SQL Server) - 避免用视图封装聚合逻辑——视图不存统计信息,优化器只能猜
- 在关键聚合字段(如
user_id)上建索引,既加速聚合过程,也为后续JOIN提供排序依据
真正卡住性能的,往往不是算法名字,而是优化器看到的“数据长相”——而统计信息,就是它唯一能读的简历。










