SQL更新操作导致索引失效_避免对WHERE条件列进行计算

3次阅读

WHERE 条件中对索引列使用函数或运算会导致索引基本不走,因破坏 B + 树有序性;正确做法是保持左侧为裸列名,将计算移至右侧或改用范围查询。

SQL 更新操作导致索引失效_避免对 WHERE 条件列进行计算

WHERE 里对索引列做计算,索引直接不走

MySQL、PostgreSQL、SQL Server 都一样:只要在 WHERE 条件里对已建索引的列用了函数或运算,优化器大概率放弃使用该索引。不是“可能失效”,是“基本不走”。比如 WHERE YEAR(create_time) = 2024,哪怕 create_time 上有索引,也大概率全表扫描。

  • 常见错误现象:EXPLAIN 显示 type=ALLkey=NULL,执行变慢,CPU/IO 暴涨
  • 根本原因:索引是按原始值有序存储的,而 YEAR()UPPER()col + 1 等操作破坏了有序性,无法用 B + 树快速定位
  • 不是所有函数都绝对禁止——像 MySQL 8.0+ 对 CAST(col AS DATE) 在某些场景下能用索引,但别赌,优先避开

把计算提到右边,让左边保持纯列名

最稳妥的写法,是把计算逻辑从索引列上“剥离开”,挪到等号右侧或子查询里。核心原则:WHERE 左侧必须是裸列名(或列名加简单前缀如 table.col)。

  • 错的:WHERE DATE(create_time) = '2024-01-01'DATE() 作用于索引列
  • 对的:WHERE create_time >= '2024-01-01' AND create_time → 左侧仍是 <code>create_time
  • 字符串场景同理:WHERE UPPER(name) = 'ABC' 改成 WHERE name = 'abc'(配合大小写敏感索引或 COLLATE utf8mb4_0900_as_cs
  • 如果业务真需要模糊前缀匹配,用 LIKE 'prefix%' 而非 SUBSTR(col, 1, 5) = 'prefix'

更新语句里的 WHERE 同样适用这条铁律

UPDATE 不是特例——它底层仍依赖 WHERE 的执行计划。一旦 WHERE 失效,不仅慢,还可能锁全表(尤其在 RR 隔离级别下),引发阻塞。

  • 危险写法:UPDATE users SET status = 1 WHERE DATE(last_login) = CURDATE() → 可能锁住几百万行
  • 安全写法:UPDATE users SET status = 1 WHERE last_login >= CURDATE() AND last_login
  • 注意 CURDATE() 是函数,但它没作用在列上,不影响索引;真正关键的是 last_login 左侧是否“干净”
  • 批量更新时,务必先 EXPLAIN 对应的 SELECT 语句,确认 key 和 rows 量级合理

特殊情况:函数索引不是万能解药

MySQL 8.0+、PostgreSQL 支持函数索引(如 CREATE INDEX idx_year ON t (YEAR(create_time))),但它只对 ** 完全匹配该函数表达式 ** 的查询生效。

  • 你建了 YEAR(create_time) 索引,那只有 WHERE YEAR(create_time) = 2024 才能用;换成 YEAR(create_time) > 2023WHERE create_time BETWEEN …… 依然无效
  • 函数索引会增加写开销和存储,且无法覆盖范围查询,实用性有限
  • 更隐蔽的坑:JSON 字段里取值如 json_col->>'$.id',即使建了函数索引,MySQL 对它的优化仍不稳定,生产环境慎用

真正难的不是知道“别在 WHERE 里算”,而是每次写条件前下意识检查左侧是不是赤裸裸的列名——尤其是拼接动态 SQL 或用 ORM 自动生成时,很容易漏看一层包装。

星耀云
版权声明:本站原创文章,由 星耀云 2026-03-20发表,共计1452字。
转载说明:转载本网站任何内容,请按照转载方式正确书写本站原文地址。本站提供的一切软件、教程和内容信息仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。
text=ZqhQzanResources