由于最后 WHERE 条件以及排序均针对最左主表 , 因此可以先对 my_order 排序提前缩小数据量再做左连接 。SQL 重写后如下 , 执行时间缩小为1毫秒左右 。
文章插图
再检查执行计划:子查询物化后(select_type=DERIVED)参与 JOIN 。虽然估算行扫描仍然为90万 , 但是利用了索引以及 LIMIT 子句后 , 实际执行时间变得很小 。
+----+-------------+------------+--------+---------------+---------+---------+-------+--------+----------------------------------------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+------------+--------+---------------+---------+---------+-------+--------+----------------------------------------------------+| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 15 | Using temporary; Using filesort || 1 | PRIMARY | u | eq_ref | PRIMARY | PRIMARY | 4 | o.uid | 1 | NULL || 1 | PRIMARY | p | ALL | PRIMARY | NULL | NULL | NULL | 6 | Using where; Using join buffer (Block Nested Loop) || 2 | DERIVED | o | index | NULL | idx_1 | 5 | NULL | 909112 | Using where |+----+-------------+------------+--------+---------------+---------+---------+-------+--------+----------------------------------------------------+8、中间结果集下推
再来看下面这个已经初步优化过的例子(左连接中的主表优先作用查询条件):
文章插图
那么该语句还存在其它问题吗?不难看出子查询 c 是全表聚合查询 , 在表数量特别大的情况下会导致整个语句的性能下降 。
其实对于子查询 c , 左连接最后结果集只关心能和主表 resourceid 能匹配的数据 。因此我们可以重写语句如下 , 执行时间从原来的2秒下降到2毫秒 。
文章插图
但是子查询 a 在我们的SQL语句中出现了多次 。这种写法不仅存在额外的开销 , 还使得整个语句显的繁杂 。使用 WITH 语句再次重写:
文章插图
总结
数据库编译器产生执行计划 , 决定着SQL的实际执行方式 。但是编译器只是尽力服务 , 所有数据库的编译器都不是尽善尽美的 。
上述提到的多数场景 , 在其它数据库中也存在性能问题 。了解数据库编译器的特性 , 才能避规其短处 , 写出高性能的SQL语句 。
程序员在设计数据模型以及编写SQL语句时 , 要把算法的思想或意识带进来 。
编写复杂SQL语句要养成使用 WITH 语句的习惯 。简洁且思路清晰的SQL语句也能减小数据库的负担。
作者:db匠 , 如有侵权 , 请联系删除 。来源:https://yq.aliyun.com/articles/72501
推荐阅读
- 一文读懂MySQL的ACID原理
- MySQL的五种日期和时间类型
- MySQL 性能优化的 21 个最佳实践
- 微信群营销常见的误区
- mysql数据库常用语句总结
- 如何确定JavaScript中this的指向?这里有5中常见的分类
- C语言如何操作MySQL数据库?
- 详解SQL Server四个系统库--Master、Msdb、Model和Tempdb
- linux的那些常见目录
- MySQL数据库中,同样是删除数据,truncate与delete语句的区别