where子句中对字段使用函数(如upper(name)、date(created_at))会导致索引失效,应改为将函数移至右侧或用范围条件替代,以避免全表扫描。

WHERE 子句里用函数导致索引失效
只要 WHERE 条件对字段套了函数,比如 WHERE UPPER(name) = 'JOHN' 或 WHERE DATE(created_at) = '2024-01-01',绝大多数数据库(MySQL、PostgreSQL、SQL Server)都会跳过索引,走全表扫描。
真正该做的不是“怎么让函数走索引”,而是绕开它:
- 把函数移到右边:用
WHERE name = UPPER('john')替代UPPER(name) = 'JOHN' - 用范围替代函数:用
WHERE created_at >= '2024-01-01' AND created_at 替代 <code>DATE(created_at) = '2024-01-01' - 建函数索引(仅 PostgreSQL / MySQL 8.0+ 支持):
CREATE INDEX idx_upper_name ON users (UPPER(name)),但得确认业务真需要且查询频率高
JOIN 多表时驱动表选错,性能断崖式下跌
数据库执行 JOIN 不是“随便挑一个表开始”,而是先选驱动表(outer table),再用它的每行去查被驱动表(inner table)。如果驱动表是 100 万行的大表,而被驱动表只有 100 行,但没走索引——那就等于做 100 万次随机 I/O。
判断和干预的关键点:
- 看执行计划里的
EXPLAIN输出,重点关注type字段:要是出现ALL或index,说明没走好索引 - 小表优先做驱动表:显式用
STRAIGHT_JOIN(MySQL)或在FROM中把预期的小表放前面 - 确保被驱动表的关联字段有索引:比如
LEFT JOIN orders o ON u.id = o.user_id,必须在orders.user_id上建索引
GROUP BY + 聚合后还要排序,ORDER BY 别乱加
GROUP BY 本身不保证顺序,但很多人会顺手加个 ORDER BY id,结果发现慢得离谱——尤其当分组后结果集大、又没索引支撑排序字段时。
Hishop.5.2.BETA2版主要更新: [修改] 进一步优化了首页打开速度 [修改] 美化了默认模板 [修改] 优化系统架构,程序标签及SQL查询效率,访问系统页面的速度大大提高 [修改] 采用了HTML模板机制,实现了前台模板可视化编辑,降低模板制作与修改的难度. [修改] 全新更换前后台AJAX技术框架,提升了用户操作体验. 店铺管理 [新增] 整合TQ在线客服 [修改] 后台广告位增加
更实际的做法:
- 如果只是想按分组字段排序,直接写
GROUP BY x ORDER BY x,多数引擎能复用分组的临时排序结构 - 如果要按聚合结果排序,比如
ORDER BY COUNT(*) DESC,就别指望索引了,但可以加LIMIT控制输出量,避免排序整个中间结果集 - 避免在
SELECT *后用GROUP BY:MySQL 5.7+ 严格模式下会报错,老版本可能返回不可靠值;明确列出所有非聚合字段
子查询写成 WHERE IN (SELECT ...) 就容易卡死
这个写法看着直观,但 MySQL 在 5.6 及更早版本里,IN 子查询默认是“循环执行”:外层每一条,都跑一遍子查询。哪怕子查询只返回 10 行,外层 10 万行就是 100 万次执行。
换种写法,效果立竿见影:
- 改用
JOIN:把子查询提前物化成临时表,再关联,执行一次即可 - 用
EXISTS替代(尤其子查询带条件时):WHERE EXISTS (SELECT 1 FROM logs l WHERE l.user_id = u.id AND l.status = 'error') - 确认子查询是否真的需要实时计算:能预聚合的,就用
CREATE TEMPORARY TABLE先存好结果
复杂点在于,不同数据库对子查询的优化策略差异很大——PostgreSQL 通常自动转成哈希连接,MySQL 则高度依赖写法和版本。别信“语法一样性能就一样”。









