索引是否生效取决于字段是否有合适索引及是否满足最左前缀、无隐式转换等条件,而非使用>、<还是BETWEEN;三者在执行计划中基本等价,真正影响索引使用的包括函数包裹、OR拆分、复合索引中范围查询位置不当、NULL参与比较、统计信息不准、时区与精度不一致等因素。

WHERE条件里用 > < 还是 BETWEEN,索引都有效吗?
有效,但前提是字段上有单列索引或复合索引的最左前缀匹配,且没有隐式类型转换、函数包裹或NULL干扰。很多人以为 BETWEEN 更“智能”所以更优,其实它和 >= <= 在执行计划里几乎等价,MySQL/PostgreSQL 都会走相同索引访问路径。
真正影响索引是否生效的,不是写法本身,而是:
- 字段是否被计算或转换(比如 WHERE YEAR(created_at) > 2022)
- 条件是否包含 OR 拆分导致索引失效
- 复合索引中范围查询字段不在最右位置(如索引是 (a, b, c),WHERE a = 1 AND c > 10 就只能用上 a)
-
BETWEEN是闭区间语法糖,语义清晰,可读性略好,但不会触发额外优化 -
>/<更灵活,适合开区间或非对称范围(如created_at > '2023-01-01') - 如果字段允许 NULL,
BETWEEN NULL AND X或X <= NULL全部不成立——NULL 参与任何比较都返回 UNKNOWN,直接过滤掉整行
为什么加了索引,> 查询还是全表扫描?
常见原因是数据分布或统计信息不准,让优化器误判“查太多行不如扫表”。比如一张千万级表,时间字段有 95% 数据落在最近 7 天,你写 WHERE updated_at > '2024-06-01',优化器发现要回表取 800 万行,可能直接放弃索引。
- 用
EXPLAIN看rows和key字段:如果key为空但表有索引,大概率是优化器拒绝使用 - 强制走索引(仅调试):
SELECT * FROM t USE INDEX (idx_updated_at) WHERE updated_at > '2024-06-01' - 更新统计信息:
ANALYZE TABLE t(MySQL),或VACUUM ANALYZE t(PostgreSQL) - 检查字段类型是否一致:比如索引建在
DATETIME上,但查询传的是字符串'2024-06-01',MySQL 可能隐式转成DATE导致索引失效
BETWEEN 在复合索引里的位置陷阱
复合索引顺序决定范围查询能用几层。假设索引是 (status, created_at, user_id):
-
WHERE status = 'active' AND created_at BETWEEN '2024-01-01' AND '2024-06-01'→ 能用上全部两层(status精确匹配 +created_at范围) -
WHERE created_at BETWEEN '2024-01-01' AND '2024-06-01'→ 只能全索引扫描或不用索引(因为没匹配最左列status) -
WHERE status = 'active' AND user_id > 1000→ 只能用上status,user_id因为跳过了中间的created_at,无法利用索引排序
简单说:**范围查询之后的字段,索引就断了**。别指望 ORDER BY user_id 在上面第三个例子里还能走索引排序。
日期范围查询最容易忽略的时区和精度问题
数据库时区设置和字段精度不一致,会让明明该走索引的查询“看起来失效”。例如:
- 字段是
TIMESTAMP(带时区),应用传入的字符串是本地时区,而 MySQL server_time_zone 是 UTC,导致实际查询条件偏移 - 字段是
DATETIME(6)(微秒精度),但查询只传到秒级:'2024-06-01 12:00:00',MySQL 会补零成'2024-06-01 12:00:00.000000',通常不影响索引,但如果用>写成> '2024-06-01',实际变成> '2024-06-01 00:00:00',逻辑没错,但可能查出远超预期的数据量,触发优化器弃用索引 - PostgreSQL 对
TSTZRANGE类型支持原生范围类型,但普通BETWEEN不会自动利用其 GiST 索引,得显式建USING GIST (tsrange(start_time, end_time))
时区和精度不是语法问题,是数据语义问题——索引再快,查错时间范围也没意义。










