首页/数据/SQL 速查/列上做运算导致索引失效
性能与执行计划72 条

列上做运算导致索引失效

WHERE 里对索引列做函数或运算时,B 树无法定位范围:改写成对常量做运算,或使用函数索引。

SQL 速查数据更新于 2026-10-13

列上做运算为什么会失效

WHERE 里对索引列做函数或运算时,B+ 树无法利用列的有序性定位范围,索引就用不上了。解决思路有两条:把运算挪到常量一侧改写条件,或建立表达式索引让索引本身保存计算后的值。

基本信息

失效原因索引按列值排序,列被运算后顺序不再对应
典型场景日期函数、大小写转换、CAST、JSON 取值、字符串截取
改写原则保持索引列“裸用”,把运算移到常量一侧
MySQL 函数索引8.0.13+ 支持 ((表达式)) 语法的函数索引
PostgreSQLCREATE INDEX … ((表达式)) 原生支持
查询一致性查询表达式必须与索引表达式完全一致才能命中

条件改写示例

条件改写示例
-- 原写法:列上做 YEAR() 运算,索引失效
SELECT * FROM orders WHERE YEAR(created_at) = 2024;

-- 改写为范围条件,可走 created_at 上的索引
SELECT * FROM orders
WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';

-- 大小写不敏感匹配:不要用 UPPER(col),用不区分大小写的排序规则
SELECT * FROM users WHERE name = 'tom';   -- 列为 _ci 排序规则时天然可用索引

表达式索引写法

表达式索引写法
-- MySQL 8.0.13+ 函数索引:表达式要用括号包起来
ALTER TABLE orders ADD INDEX idx_year ((YEAR(created_at)));
SELECT * FROM orders WHERE YEAR(created_at) = 2024;   -- 与索引表达式一致才命中

-- PostgreSQL 表达式索引
CREATE INDEX idx_orders_year ON orders ((EXTRACT(YEAR FROM created_at)));
-- 查询里也要用同样的 EXTRACT 写法,换成别的等价形式不会命中

失效写法与替代

失效写法问题替代写法
YEAR(col) = 2024列被函数包裹col >= '2024-01-01' AND col < '2025-01-01'
DATE(col) = '2024-06-01'同上col >= '2024-06-01' AND col < '2024-06-02'
UPPER(col) = 'ABC'列被函数包裹用不区分大小写的排序规则,或建表达式索引
col + 1 = 10列参与算术运算col = 9
CAST(col AS CHAR) = '10'类型转换包住列把常量转成列的类型
LIKE '%abc'前置通配无法定位补一列后缀列,或改用全文检索

典型使用场景

  • 日期条件改写:把按年、按月的过滤换成范围条件
  • 大小写匹配:优先用排序规则而不是把列包进 UPPER
  • 类型统一:把常量转成列的类型,而不是反过来
  • 表达式索引评估:确属高频查询再建,避免索引膨胀
  • 慢查询排查:先看 WHERE 里的列是不是被运算了
  • 跨库迁移:检查原来依赖函数索引的语句在新库是否还有对应索引
建了表达式索引却仍写原条件是最常见的白干:索引里存的是表达式的值,查询必须写出一模一样的表达式才会被匹配,上线前先用执行计划确认。
在列上套函数不仅让索引失效,还可能引入隐式类型转换与结果偏差。比较前先确认两侧类型,把转换放在常量一侧,既安全也更快。

易错点

  • 在过滤条件里给列包函数:索引失效退化为全表扫描,改成对常量一侧做运算
  • 表达式索引与查询写法不一致:索引存在却用不上,必须用执行计划验证
  • 为绕开改写到处加表达式索引:索引数量上升带来写放大,先评估查询频率
  • 用 DATE(col) 做按天过滤:不仅索引失效,语义上也容易漏数据,改用左闭右开区间

数据来源与参考

  • MySQL 8.0 Reference Manual · Functional Indexes
  • PostgreSQL Documentation §11.7 Indexes on Expressions
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌