必须将时间范围条件写在WHERE子句而非JOIN的ON子句中,否则分区裁剪不触发、索引大概率失效;正确做法是WHERE中写o.event_time >= ?,并配合复合索引(user_id, status, event_time)。

直接加时间范围条件到 WHERE,否则分区裁剪不触发、索引大概率失效——这不是“建议”,是 MySQL/PostgreSQL 的硬性行为规则。
为什么ON里写时间范围常被误用?
很多人想在 LEFT JOIN ... ON 里加 o.event_time BETWEEN p.valid_from AND p.valid_until,以为能“提前过滤右表”。但实际效果取决于右表是否带分区键或索引支撑:
- 如果右表按
valid_from分区,且ON中含该字段的等值或范围约束(如p.valid_from ),才可能触发分区裁剪 - 若只写
o.event_time BETWEEN p.valid_from AND p.valid_until,而右表没建(valid_from, valid_until)复合索引,优化器大概率放弃索引,走嵌套循环全扫 -
LEFT JOIN下,这个条件还可能导致右表匹配失败时返回NULL,后续再加WHERE p.id IS NOT NULL就变相转成INNER JOIN,语义已偏移
WHERE 时间条件必须显式、无函数、开区间
分区裁剪和索引生效的前提,是优化器能静态推导出可排除的分区。任何干扰字段原始值的操作都会断掉这条链路:
- ❌ 错误写法:
WHERE DATE(o.event_time) = '2026-08-01'→ 函数导致分区和索引同时失效 - ❌ 错误写法:
WHERE o.event_time + INTERVAL 1 DAY >= '2026-08-02'→ 表达式破坏字段可定位性 - ✅ 正确写法:
WHERE o.event_time >= '2026-08-01' AND o.event_time → 开区间、无函数、类型一致 - ⚠️ 注意:
BETWEEN '2026-08-01' AND '2026-08-01 23:59:59'容易因微秒/纳秒精度丢数据,不推荐
区间JOIN别硬写BETWEEN,试试窗口函数
MySQL 8.0+ 不支持原生区间 JOIN 优化,硬写 ON o.t BETWEEN p.from AND p.to 极易触发 type: ALL 扫描。更可控的做法是两步走:
- 先做宽泛连接:
FROM orders o JOIN prices p ON o.user_id = p.user_id AND o.event_time >= p.valid_from - 再用
ROW_NUMBER() OVER (PARTITION BY o.order_id ORDER BY p.valid_from DESC)标序,外层取rn = 1 - 关键:必须有
INDEX (user_id, valid_from),且ORDER BY字段要是索引前缀,否则排序代价爆炸 - 别用
GROUP BY o.order_id HAVING MAX(p.valid_from)替代——容易丢字段、结果不可靠
复合索引顺序必须贴合扫描路径
建了 INDEX (event_time) 还慢?大概率是因为查询实际走的是 WHERE event_time > ? AND status = ? —— 单列索引覆盖不了多条件。
- 高选择性字段(如
user_id、tenant_id)放最左,范围字段(如event_time)放右 - 区间 JOIN 场景下,优先建
INDEX (valid_from, valid_until),而不是反过来 - 如果查询还带
AND status IN ('active', 'pending'),索引应扩展为(user_id, status, event_time) - 用
EXPLAIN看key是否命中、rows是否明显下降,别凭感觉
真正卡住性能的,往往不是 JOIN 本身,而是时间条件没写对位置、没写对形式、没配上对应索引——这三者缺一不可。尤其在分区表上,漏掉 WHERE 时间条件,等于白建分区。

















