如何优化以下UPDATE查询语句以获得更快的执行结果?
先别急,我们一步步来拆解和优化这个查询——从理清逻辑歧义到消除性能瓶颈,让你的UPDATE跑得更快:
1. 先修正WHERE子句的逻辑歧义
原语句的WHERE条件TE.vendido = 0 or TE.vendido is null or TE.vendido = 0 and TP.id_ubicacion is null or TE.vendido is null and TP.id_ubicacion is null因为AND优先级高于OR,实际执行逻辑大概率和你预期的不一致。用括号明确分组是关键,假设你的真实需求是:
统计
TE.vendido为0或NULL的记录,或者TP.id_ubicacion为NULL时的对应记录
修正后的条件应该是:
(TE.vendido = 0 OR TE.vendido IS NULL) OR TP.id_ubicacion IS NULL
如果你的需求是仅当TE.vendido为0/NULL,同时TP.id_ubicacion可为空,可以调整为:
(TE.vendido = 0 OR TE.vendido IS NULL) AND (TP.id_ubicacion IS NULL OR TP.id_ubicacion IS NOT NULL)
(注:后面的AND条件其实冗余,只是用来演示分组的重要性)
2. 修复子查询的分组错误
原子查询里GROUP BY TP.id但选择TP.codigo1,这会导致同一个codigo1对应多个TP.id时,子查询返回多行重复的codigo1结果,UPDATE时会重复更新同一条TblPartes记录——既浪费性能又可能导致逻辑错误。正确的分组应该是GROUP BY TP.codigo1,因为我们最终是按codigo1更新stock字段。
3. 添加强力索引(性能提升核心)
索引是加速查询的关键,针对这个场景建议创建以下索引:
- 给
TblExistencias建组合索引:
这个索引会同时加速JOIN(用到CREATE INDEX idx_te_idcodigo_vendido ON TblExistencias(id_codigo, vendido);id_codigo)和WHERE过滤(用到vendido)的过程,避免全表扫描。 - 给
TblPartes建覆盖索引:
覆盖子查询用到的CREATE INDEX idx_tp_id_codigo1_ubicacion ON TblPartes(id, codigo1, id_ubicacion);id(JOIN关联)、codigo1(分组和UPDATE关联)、id_ubicacion(WHERE过滤)三个字段,避免数据库频繁回表查询原始数据。
4. 重构后的高效UPDATE语句
结合以上优化点,最终的查询语句如下:
UPDATE TblPartes tp JOIN ( SELECT tp_inner.codigo1, COUNT(te.id_codigo) AS stock_count FROM TblPartes tp_inner LEFT JOIN TblExistencias te ON tp_inner.id = te.id_codigo WHERE (te.vendido = 0 OR te.vendido IS NULL) OR tp_inner.id_ubicacion IS NULL GROUP BY tp_inner.codigo1 ) te_summary ON tp.codigo1 = te_summary.codigo1 SET tp.stock = te_summary.stock_count;
额外小技巧(针对MySQL 8.0+)
如果你的数据库支持窗口函数,可以尝试用它替代子查询分组,简化逻辑的同时可能进一步提升性能:
UPDATE TblPartes tp JOIN ( SELECT DISTINCT tp_inner.codigo1, COUNT(te.id_codigo) OVER (PARTITION BY tp_inner.codigo1) AS stock_count FROM TblPartes tp_inner LEFT JOIN TblExistencias te ON tp_inner.id = te.id_codigo WHERE (te.vendido = 0 OR te.vendido IS NULL) OR tp_inner.id_ubicacion IS NULL ) te_summary ON tp.codigo1 = te_summary.codigo1 SET tp.stock = te_summary.stock_count;
(注意加DISTINCT避免重复更新,实际测试后选择更高效的版本即可)
内容的提问来源于stack exchange,提问作者Martin




