相关子查询的索引依赖在被驱动的子查询表上,如order_items表需为order_id或(order_id, qty)建索引,外层表关联字段若非主键也需索引,且避免对索引列使用函数操作。

相关子查询的索引依赖在哪张表上
相关子查询的性能瓶颈几乎总在**被驱动的子查询表**(即子查询中涉及的表),而不是外层表。因为数据库要为外层每一行,重复执行一次子查询——这意味着子查询本身的执行效率必须极高,否则整体耗时会呈线性放大。
典型结构如:SELECT * FROM orders o WHERE EXISTS (SELECT 1 FROM order_items i WHERE i.order_id = o.id AND i.qty > 10)。这里 order_items 是子查询表,o.id 是外层引用值,i.order_id 必须有索引,否则每次都要全表扫描 order_items。
-
order_items.order_id必须建索引(最好是INDEX idx_order_id ON order_items(order_id)) - 若子查询还有额外过滤条件(如
i.qty > 10),可考虑扩展为复合索引:INDEX idx_order_id_qty ON order_items(order_id, qty) - 外层表
orders的id字段本身是主键,无需额外索引;但若用的是非主键关联(如o.ref_no),则该字段也需索引
EXISTS vs IN:索引要求有何不同
EXISTS 和 IN 在索引使用逻辑上本质一致:都依赖子查询表的关联字段索引。但实际行为有细微差别,影响优化器是否启用索引:
-
EXISTS只需找到一行匹配即终止,对索引友好,尤其适合“是否存在”类判断 -
IN子查询若返回空集或 NULL,可能触发全表扫描(MySQL 5.7+ 已改善,但旧版本仍需警惕) - 当子查询结果集较大(比如上千行),
IN (subquery)可能被优化器转为临时表 + hash join,此时order_items.order_id索引依然有效,但临时表构建开销会上升 - 更稳妥的做法:用
EXISTS替代IN,并确保子查询中WHERE条件的左侧字段已建索引
容易被忽略的索引失效场景
即使你给 order_items.order_id 建了索引,以下写法仍会让索引失效:
- 子查询中对索引列做函数操作:
WHERE YEAR(i.created_at) = 2024→ 改为范围查询:WHERE i.created_at >= '2024-01-01' AND i.created_at - 隐式类型转换:
WHERE i.order_id = '123'(字段是INT)→ 改为:WHERE i.order_id = 123 - 使用
OR连接非索引字段:WHERE i.order_id = o.id OR i.sku LIKE '%ABC%'→ 拆成UNION ALL或重写逻辑 - 子查询里用了
SELECT *或未加WHERE限制,导致优化器放弃索引选择全表扫描
如何验证索引是否生效
别猜,直接看执行计划。对整个语句运行 EXPLAIN,重点观察子查询部分的 key 和 rows:
- 如果子查询对应行的
key显示索引名(如idx_order_id),且rows是个位数或百位数,说明索引命中 - 如果
key是NULL,type是ALL,就是没走索引 - 注意
Extra列:出现Using where; Using index表示覆盖索引,最优;出现Using temporary; Using filesort说明子查询内部还有排序/去重,需进一步优化
真正麻烦的不是建不建索引,而是子查询里混进了无法下推的计算、类型转换或模糊匹配——这些会让索引形同虚设。

















