导读:本期聚焦于小伙伴创作的《为什么SQL更新语句要避免对索引列执行函数操作?性能调优技巧详解》,敬请观看详情。一条本该秒级完成的UPDATE在监控里跑了十几秒,慢查询日志指向了WHERE条件里对索引列套了DATE函数。数据库优化器面对这种写法,往往直接放弃走索引而选择全表扫描,因为对列计算后的值和原索引树里的有序数据已经对不上。把函数从列上挪到比较常量一侧,或者改用范围条件,执行计划会重新命中索引,更新行数相同时耗时能降一个数量级。本文从执行计划差异、改写方式、以及批量更新时的注意点展开,说明如何在不改业务逻辑的前提下消除这类隐性性能坑。

在写SQL更新语句时,很多人习惯把时间字段用函数处理后再做条件判断,比如按天过滤记录。这种写法表面上逻辑正确,但放到有索引的表上,常常让更新性能断崖式下跌。理解优化器对索引的使用规则,就能用很小的改动换回明显的效率提升。

为什么SQL更新语句要避免对索引列执行函数操作?性能调优技巧详解

一、为什么对索引列用函数会让更新变慢

数据库索引本质上是按列原始值构建的有序结构,例如B+树索引会依据create_time字段的真实时间戳排序。当更新语句的WHERE条件写成WHERE DATE(create_time) = '2023-05-01'时,优化器需要先对每一行的create_time调用DATE函数,得到结果后再和常量比较。由于索引里存的是原始值,函数计算后的结果并不在索引顺序中,优化器无法利用索引做快速定位,只能选择全表扫描,逐行计算函数并判断。

全表扫描意味着即使你只想更新一百行,数据库也要读取整张表的数据页,在大数据表上这会直接拖垮更新性能,还会增加锁持有时间,影响并发。我们可以通过EXPLAIN看到,这类语句的type列通常显示ALL,key列为NULL,而改写后则能见到ref或range以及具体的索引名。

二、常见的错误写法与正确改写

下面是一段典型的慢更新语句,在百万级订单表上对索引列使用函数:

-- 错误写法:对索引列create_time使用DATE函数
UPDATE orders
SET status = 'closed'
WHERE DATE(create_time) = '2023-05-01';

这个语句会让优化器忽略create_time上的索引。正确的思路是把函数从列上移开,改为对比较值做范围限定,让原始列可以直接比对:

-- 正确写法:使用范围条件,保留索引列原始形态
UPDATE orders
SET status = 'closed'
WHERE create_time >= '2023-05-01 00:00:00'
  AND create_time < '2023-05-02 00:00:00';

改写后,优化器能识别这是一个闭开区间,直接通过索引定位到对应时间片的数据页,更新所涉及行数相同,但逻辑读从全表级别降到仅命中索引叶子节点附近。如果业务只需要日期部分,也可以提前在应用层算出起止时间字符串再传入,避免在SQL里写函数。

其他容易踩坑的函数场景

  • 对数值索引列使用ABS(amount) = 100,应改为amount = 100 OR amount = -100或调整业务不取绝对值。
  • 对字符串索引列使用UPPER(name) = 'TOM',可改为在存入时就统一大小写,或建一个函数索引(部分库支持)。
  • 使用YEAR(created_at)过滤年份,应改为created_at >= '2023-01-01' AND created_at < '2024-01-01'

三、函数索引是否是万能解

有些数据库如MySQL 8.0、PostgreSQL支持创建函数索引,也就是对DATE(create_time)单独建索引。语法类似:

-- MySQL 8.0 函数索引示例
CREATE INDEX idx_date_ct ON orders ((DATE(create_time)));

函数索引确实能让原语句重新走索引,但它会占用额外存储空间,并且在写入时数据库要同时维护原列索引和函数索引,更新和插入开销变大。对于只是偶尔执行的更新,不如直接改写SQL;对于高频按函数查询的场景,函数索引才更划算。调优时应先尝试改写,再评估是否建函数索引。

四、批量更新时的补充建议

即便改写了条件,一次更新几十万行仍可能锁表过久。建议结合limit分批,并利用索引列排序来让每批都走索引:

-- 分批更新示例,利用主键和索引列控制范围
UPDATE orders
SET status = 'closed'
WHERE create_time >= '2023-05-01 00:00:00'
  AND create_time < '2023-05-02 00:00:00'
  AND id > 0
ORDER BY id
LIMIT 1000;

每批千行左右,循环执行直到受影响行为零,可以把长事务拆短,降低主从延迟和锁冲突。同时务必确认WHERE里所有条件字段组合起来仍能命中索引,否则分批也救不了全表扫描。掌握这些技巧后,面对类似更新慢的问题就能快速定位是否为索引列被函数包裹导致。

SQL调优索引列函数操作修改时间:2026-08-02 17:03:25

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。