WHERE time BETWEEN ‘2024-01-01’ AND ‘2024-06-30’ 未触发分区修剪,因表按 DATE(time) 分区而查询条件作用于 time 列本身,类型精度不匹配导致优化器无法映射到具体分区。

为什么 WHERE time BETWEEN '2024-01-01' AND '2024-06-30' 没触发分区修剪?
分区修剪失效,往往不是 SQL 写得不对,而是底层分区键和查询条件不匹配。比如表按 DATE(time) 分区,但查询用了 time >= '2024-01-01' AND time —— 看似等价,MySQL/PostgreSQL 却可能无法推导出可剪枝的区间,因为函数包裹了分区列。
- 必须直接使用分区列本身做比较,避免
YEAR()、DATE()、TO_DAYS()等函数包裹 - 字符串字面量要严格匹配分区表达式类型:若按
RANGE COLUMNS(time)分区,'2024-01-01'会被隐式转为 datetime,但精度丢失可能导致边界误判 - PostgreSQL 的
LIST/RANGE分区要求条件能被 planner 静态评估;带参数的 prepared statement 在某些版本中会禁用修剪
MySQL 中 PARTITION BY RANGE COLUMNS(time) 的边界写法陷阱
用 COLUMNS 做范围分区时,VALUES LESS THAN 的值必须与列类型完全一致,且不能“跨粒度”定义。例如 time 列是 DATETIME(3),但分区边界只写 '2024-01-01',MySQL 会补零成 '2024-01-01 00:00:00.000',看似安全,实际在插入 '2024-01-01 00:00:00.123' 时可能落入错误分区。
- 边界值建议显式写出完整精度:
VALUES LESS THAN ('2024-01-01 00:00:00.000') - 避免用
UNIX_TIMESTAMP(time)分区——虽然能剪枝,但可读性差,且 timestamp 值受时区影响,运维排查困难 - 执行
EXPLAIN PARTITIONS SELECT ……后,检查partitions列是否只列出预期的几个分区名;如果显示NULL或全量分区,则修剪失败
PostgreSQL CREATE TABLE …… PARTITION BY RANGE (time) 下的约束与剪枝关系
PG 不像 MySQL 那样自动依赖分区定义来剪枝,它更依赖每个子表上的 CHECK 约束。如果手动 CREATE TABLE …… INHERITS 或忘记 FOR VALUES FROM (……) TO (……),即使表结构看起来是分区表,查询也不会剪枝。
- 必须用
CREATE TABLE …… PARTITION OF parent FOR VALUES FROM (……) TO (……)语法建子表,PG 才能从约束反推可剪区间 - 时间字段类型要统一:父表用
TIMESTAMP WITH TIME ZONE,子表也必须同类型,否则约束无法被 planner 识别 - 对已存在数据的表加分区,需先
SET ENABLE_SEQSCAN = OFF测试剪枝效果,避免因统计信息滞后导致 plan 错误
查询里混用 OR 和多个时间范围,为什么分区修剪崩了?
WHERE (time >= '2024-01-01' AND time = '2024-05-01' AND time 这种写法,在 MySQL 8.0+ 和 PG 12+ 多数能剪,但一旦加上 <code>OR status = 'failed',整个条件就变成不可拆分的布尔表达式,修剪立即失效。
- 优先改写为
IN或UNION ALL:分别查两个时间范围再合并,确保每次查询只触达目标分区 - 避免在分区列条件上叠加函数或表达式,如
time + INTERVAL 1 DAY > '2024-01-01'—— planner 无法反解原始分区边界 - 如果业务必须支持任意时间组合,考虑在应用层预计算目标分区名,用
SELECT …… FROM t PARTITION (p202401, p202405)显式指定(MySQL)或动态拼接表名(PG)
分区修剪不是开关一开就自动生效的魔法,它是 planner 对 SQL 结构、分区定义、类型精度三者严格匹配的结果。少一个点对齐,就退回全表扫描。






























