一次SQL优化处理

背景

研发在预生产环境上线一个 SQL ,执行时间耗时 6s ,研发通过加索引、排查隐式转换等方式,经过初步优化后,耗时 2s

需求

想让 dba 给协助看下是否还有优化空间

处理过程

1、先看下面这个已经初步优化过的例子(左连接中的主表优先作用查询条件):

SELECT    a.*,

          c.transaction

FROM      (

              SELECT   resource_id

              FROM     user_desc d

                   WHERE    isdelete = 0

                   AND      user_code = 'ac160589'

                   ORDER BY sale_code limit 20) a

LEFT JOIN

          (

              SELECT   resources_id sum(ifnull(allocation, 0) * 12345) transaction

              FROM     my_resources

                   GROUP BY resources_id) c

ON        a.resource_id = c.resources_id;

 

2、该语句还存在其它问题。不难看出子查询 c 是全表聚合查询,在表数量特别大的情况下会导致整个语句的性能下降。

其实对于子查询 c ,左连接最后结果集只关心能和主表 resource_id 能匹配的数据。因此我们可以重写语句如下,执行时间从原来的 2 秒下降到 2 毫秒。

SELECT    a.*,

          c.transaction

FROM      (

                   SELECT   resource_id

                   FROM     user_desc d

                   WHERE    isdelete = 0

                   AND      user_code = 'ac160589'

                   ORDER BY sale_code limit 20) a

LEFT JOIN

          (

                   SELECT   resources_id sum(ifnull(allocation, 0) * 12345) transaction

                   FROM     my_resources r,

                            (

                                     SELECT   resource_id

                                     FROM     user_desc d

                                     WHERE    isdelete = 0

                                     AND      user_code = 'ac160589'

                                     ORDER BY sale_code limit 20) a

                   WHERE    r.resources_id = a.resources_id

                   GROUP BY resources_id) c

ON        a.resource_id = c.resources_id;

3、但是子查询 a 在我们的SQL语句中出现了多次。这种写法不仅存在额外的开销,还使得整个语句显的繁杂。使用 WITH 语句再次重写:

WITH a AS

(

         SELECT   resource_id

         FROM     user_desc d

         WHERE    isdelete = 0

         AND      user_code = 'ac160589'

         ORDER BY sale_code limit 20)

SELECT    a.*,

          c.transaction

FROM      a

LEFT JOIN

          (

                   SELECT   resources_id sum(ifnull(allocation, 0) * 12345) transaction

                   FROM     my_resources r,

                            a

                   WHERE    r.resources_id = a.resources_id

                   GROUP BY resources_id) c

ON        a.resource_id = c.resources_id;

4、总结

数据库编译器产生执行计划,决定着 SQL 的实际执行方式。但是编译器只是尽力服务,所有数据库的编译器都不是尽善尽美的。

上述提到的多数场景,在其它数据库中也存在性能问题。了解数据库编译器的特性,才能避规其短处,写出高性能的 SQL 语句。

程序员在设计数据模型以及编写 SQL 语句时,要把算法的思想或意识带进来。编写复杂 SQL 语句要养成使用 WITH 语句的习惯。简洁且思路清晰的 SQL 语句也能减小数据库的负担。


请使用浏览器的分享功能分享到微信等