非等值连接慢是因为优化器无法使用Hash Join或Merge Join,只能退化为Nested Loop,导致O(m×n)复杂度;需建复合索引如(min_amt, max_amt)、类型一致、WHERE提前过滤,并权衡查询时计算与写入时预计算。

非等值连接为什么慢:执行计划退化成嵌套循环
数据库优化器对 ON a.val BETWEEN b.low AND b.high 这类条件基本放弃使用 Hash Join 或 Merge Join,因为无法做等值分桶或排序归并。实际执行时几乎全是 Nested Loop:对左表每行,遍历右表全量比对范围是否成立。10 万 × 1000 行 = 1 亿次比较,CPU 和 I/O 压力陡增。
这不是写法错误,是 SQL 标准下非等值连接的固有代价。你看到 EXPLAIN 里 type 是 ALL 或 range、key 是 NULL,基本就确认了这点。
- PostgreSQL 可能尝试用
Bitmap Index Scan+Bitmap Heap Scan,但前提是索引能覆盖范围一侧 - MySQL 5.7 几乎不优化这类 ON 条件,8.0+ 才支持部分下推,仍依赖索引设计
- SQL Server 的
Index Seek只能单边生效(比如只用上start_time),end_time得靠过滤后筛
复合索引怎么建才真正起作用
别在 low 和 high 上各建一个单列索引——优化器通常只选一个。关键是要让索引支持“先定位起点,再剪枝终点”。
假设你写的是:SELECT * FROM events e JOIN tiers t ON e.amount BETWEEN t.min_amt AND t.max_amt,那么最有效的索引是:
CREATE INDEX idx_tiers_range ON tiers (min_amt, max_amt);
原因:min_amt 是范围下界,用于快速跳过所有 min_amt > e.amount 的行;max_amt 在复合索引中作为第二列,虽不能直接驱动查找,但能让数据库在扫描出的候选行里直接读取 max_amt 值,避免回表。
- 顺序不能颠倒:如果建
(max_amt, min_amt),e.amount对不上第一列,索引完全失效 - 字段类型要一致:比如
amount是DECIMAL(10,2),min_amt也得是同类型,否则隐式转换导致索引失效 - WHERE 提前过滤依然重要:在 JOIN 前加
WHERE e.amount >= 100,能大幅减少左表参与连接的行数
LEFT JOIN 区间匹配结果重复怎么办
一条订单可能落在多个价格档位或活动区间里,LEFT JOIN 会返回多行——这不是 bug,是语义正确性要求的结果。但业务往往只要“最高优先级档位”或“最先匹配的活动”。
常见处理方式不是改 JOIN,而是控制输出行数:
- PostgreSQL:用
DISTINCT ON (e.id) ORDER BY e.id, t.priority DESC - MySQL 8.0+:加窗口函数
ROW_NUMBER() OVER (PARTITION BY e.id ORDER BY t.priority DESC),外层筛rn = 1 - 通用兜底:用相关子查询取
(SELECT t1.tier_name FROM tiers t1 WHERE e.amount BETWEEN t1.min_amt AND t1.max_amt ORDER BY t1.priority DESC LIMIT 1),但注意性能可能更差
别用 GROUP BY e.id 配合 MAX(t.tier_name) ——字符串聚合不保证对应的是同一行的 priority,逻辑已错。
数据量大时考虑分段预处理而非硬扛 JOIN
当 tiers 表稳定(比如每月只更新一次)、events 表超大(千万级)时,硬连查每次都要算一遍,不如把区间关系固化。
思路是:给每个 event 打上档位 ID,存在新字段或物化视图里:
ALTER TABLE events ADD COLUMN tier_id INT; UPDATE events e SET tier_id = ( SELECT t.id FROM tiers t WHERE e.amount BETWEEN t.min_amt AND t.max_amt ORDER BY t.priority DESC LIMIT 1 );
后续查询直接 JOIN 或 WHERE tier_id IS NOT NULL,速度提升一个数量级。
这个操作本身慢,但只需跑一次;比起每次查询都触发百万级嵌套循环,长期看更稳。真正容易被忽略的是:非等值连接的优化,本质是在「查询时计算」和「写入时计算」之间做权衡,而不是单纯调索引。

















