• SQL中使用WITH AS提高性能和可读性


    利用WITH AS子句提高SQL的性能和可读性

    一、 原理

    WITH AS短语,也叫做子查询部分(subquery factoring),可以让你做很多事情,定义一个SQL片断,该SQL片断会被整个SQL语句所用到。

    有的时候,是为了让SQL语句的可读性更高些,也有可能是在UNION ALL的不同部分,作为提供数据的部分。
    特别对于UNION ALL比较有用。因为UNION ALL的每个部分可能相同,但是如果每个部分都去执行一遍的话,则成本太高,所以可以使用WITH AS短语,则只要执行一遍即可。如果WITH AS短语所定义的表名被调用两次以上,则优化器会自动将WITH AS短语所获取的数据放入一个TEMP表里,如果只是被调用一次,则不会。而提示materialize则是强制将WITH AS短语里的数据放入一个全局临时表里。很多查询通过这种方法都可以提高速度。

    WITH AS 子句的功能是定义SQL语句级的临时表、该临时表仅对本次执行的SQL有效。作用一、可以把复杂SQL语句按照特定的业务逻辑分成几个WITH AS临时表、再用这些临时表组成完整的SQL语句,从而提高SQL语句的可读性和编写。作用二、把一大堆重复用到的SQL语句放在with as 里面,取一个别名,后面的查询就可以用它、从而减少SQL语句的长度、从而大幅提高SQL的执行效率和可读性。

    二、利用WITH AS 字句提高SQL语句的执行性能

    1. 案例起因
    公司门店应用程序每天都要出一份报表,用来统计所有商品当天的期初库存数量、入库数量、出库数量
    及当天的期末库存数量。运行半年以后,这份报表运行越来越慢,到现在,每次运行该报表显示当天数据时需要近20秒的时间。于是开发人员找到我,希望我看看,是不是可以使该报表运行的时间更短。
    该报表就是一段SQL语句,主要由三部分组成,第一部分是计算每个商品的期初数量,第二部分是计算每个商品的当天发生(包括入库和出库的)数量,第三部分是计算每个商品的期末数量,也就是当天的余额。每个部分使用UNION ALL连接起来。
    我看到该报表,第一个感觉就是这段SQL里的每个部分都要对表进行扫描,明显成本过高。应该可以使用WITH AS进行改写。

    2. 案例说明
    首先介绍该SQL所涉及到的主要的表的结构。该表表名为fin,用来存放每天每个商品的发生数以及该商
    品的余额数。其表结构为如下所示(这里我只选取了与我们要讨论的SQL相关的部分表字段)。
    SQL> desc fin
    名称 是否为空? 类型
    ----------------------------------------- -------- ----------------------------
    。。。。。。
    DAY DATE
    SKU VARCHAR2(8)
    INQTY NUMBER(16,6)
    OUTQTY NUMBER(16,6)
    LASTQTY NUMBER(16,6)
    。。。。。。。。

    简单解释一下各个字段的含义:
    1) DAY:发生的日期。
    2) SKU:发生交易的商品代码。
    3) INQTY:商品入库数量。
    4) OUTQTY:商品出库数量。
    5) LASTQTY:商品的余额数量。

    该表中含有的记录数量为:
    SQL> SELECT count(*) FROM fin;

    COUNT(*)
    ----------
    4729319
    原来的SQL如下所示(比如查询2003年7月14日这天的记录。当然,我对该SQL做了些修改,去掉了与本文讨论无关的部分,比如显示商品名称之类的部分等):

    1. SELECT SKU,
    2. SUM(INITQTY) AS INITQTY,
    3. SUM(INQTY) AS INQTY,
    4. SUM(OUTQTY) AS OUTQTY,
    5. SUM(LASTQTY) AS LASTQTY
    6. FROM (SELECT SKU,
    7. LASTQTY AS INITQTY,
    8. 0 AS INQTY,
    9. 0 AS OUTQTY,
    10. 0 AS LASTQTY
    11. FROM FIN
    12. WHERE DAY = TO_DATE('20030713', 'yyyymmdd')
    13. UNION ALL
    14. SELECT SKU, 0 AS INITQTY, INQTY, OUTQTY, 0 AS LASTQTY
    15. FROM FIN
    16. WHERE DAY >= TO_DATE('20030714', 'yyyymmdd')
    17. AND DAY <= TO_DATE('20030714', 'yyyymmdd')
    18. UNION ALL
    19. SELECT SKU, 0 AS INITQTY, 0 AS INQTY, 0 AS OUTQTY, LASTQTY
    20. FROM FIN
    21. WHERE DAY = TO_DATE('20030714', 'yyyymmdd'))
    22. GROUP BY SKU
    SELECT SKU,
           SUM(INITQTY) AS INITQTY,
           SUM(INQTY) AS INQTY,
           SUM(OUTQTY) AS OUTQTY,
           SUM(LASTQTY) AS LASTQTY
      FROM (SELECT SKU,
                   LASTQTY AS INITQTY,
                   0       AS INQTY,
                   0       AS OUTQTY,
                   0       AS LASTQTY
              FROM FIN
             WHERE DAY = TO_DATE('20030713', 'yyyymmdd')
            UNION ALL
            SELECT SKU, 0 AS INITQTY, INQTY, OUTQTY, 0 AS LASTQTY
              FROM FIN
             WHERE DAY >= TO_DATE('20030714', 'yyyymmdd')
               AND DAY <= TO_DATE('20030714', 'yyyymmdd')
            UNION ALL
            SELECT SKU, 0 AS INITQTY, 0 AS INQTY, 0 AS OUTQTY, LASTQTY
              FROM FIN
             WHERE DAY = TO_DATE('20030714', 'yyyymmdd'))
     GROUP BY SKU
    

     

    我们来看该SQL所花费的时间为:
    SQL> set timing on
    SQL> /
    。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
    SKU INITQTY INQTY OUTQTY LASTQTY
    -------- ---------- ---------- ---------- ----------
    00106162 0 0 12 60
    00106467 0 20 10 60
    已选择956行。

    已用时间: 00: 00: 19.08

    然后,我们来对该SQL进行改写一番,如下所示:

    1. WITH RESULT AS
    2. (SELECT / *+ materialize */
    3. DAY, SKU, INQTY, OUTQTY, LASTQTY
    4. FROM FIN
    5. WHERE DAY >= TO_DATE('20030713', 'yyyymmdd')
    6. AND DAY <= TO_DATE('20030714', 'yyyymmdd'))
    7. SELECT SKU, SUM(INITQTY) AS INITQTY, SUM(INQTY) AS INQTY, SUM(OUTQTY) AS OUTQTY, SUM(LASTQTY) AS LASTQTY
    8. FROM (SELECT SKU,
    9. LASTQTY AS INITQTY,
    10. 0 AS INQTY,
    11. 0 AS OUTQTY,
    12. 0 AS LASTQTY
    13. FROM RESULT
    14. WHERE DAY = TO_DATE('20030713', 'yyyymmdd')
    15. UNION ALL
    16. SELECT SKU, 0 AS INITQTY, INQTY, OUTQTY, 0 AS LASTQTY
    17. FROM RESULT
    18. WHERE DAY = TO_DATE('20030714', 'yyyymmdd')
    19. UNION ALL
    20. SELECT SKU, 0 AS INITQTY, 0 AS INQTY, 0 AS OUTQTY, LASTQTY
    21. FROM RESULT
    22. WHERE DAY = TO_DATE('20030714', 'yyyymmdd'))
    23. GROUP BY SKU
    WITH RESULT AS
     (SELECT / *+ materialize */
       DAY, SKU, INQTY, OUTQTY, LASTQTY
        FROM FIN
       WHERE DAY >= TO_DATE('20030713', 'yyyymmdd')
         AND DAY <= TO_DATE('20030714', 'yyyymmdd'))
    SELECT SKU, SUM(INITQTY) AS INITQTY, SUM(INQTY) AS INQTY, SUM(OUTQTY) AS OUTQTY, SUM(LASTQTY) AS LASTQTY
      FROM (SELECT SKU,
                   LASTQTY AS INITQTY,
                   0       AS INQTY,
                   0       AS OUTQTY,
                   0       AS LASTQTY
              FROM RESULT
             WHERE DAY = TO_DATE('20030713', 'yyyymmdd')
            UNION ALL
            SELECT SKU, 0 AS INITQTY, INQTY, OUTQTY, 0 AS LASTQTY
              FROM RESULT
             WHERE DAY = TO_DATE('20030714', 'yyyymmdd')
            UNION ALL
            SELECT SKU, 0 AS INITQTY, 0 AS INQTY, 0 AS OUTQTY, LASTQTY
              FROM RESULT
             WHERE DAY = TO_DATE('20030714', 'yyyymmdd'))
     GROUP BY SKU
    


    我们来看修改后的SQL所花费的时间为:
    SQL> set timing on
    SQL> /
    。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
    SKU INITQTY INQTY OUTQTY LASTQTY
    -------- ---------- ---------- ---------- ----------
    00106162 0 0 12 60
    00106467 0 20 10 60
    已选择956行。

    已用时间: 00: 00: 06.06

    从这里可以看到,通过WITH AS可以从20秒降低到6秒,几乎提高了65%的性能。


    三、利用WITH AS 字句提高SQL语句的可读性

    1. WITH WD AS
    2. (SELECT DID, ARG(SALARY) 平均工资 FROM WORK GROUP BY DID),
    3. EM AS
    4. (SELECT EMP.*, W.SALARY FROM EMP LEFT JOIN WORK W ON EMP.EID = W.EID)
    5. SELECT * FROM WD, EM WHERE WD.DID = EM.DID AND WD.平均工资 > EM.SALARY
    WITH WD AS
     (SELECT DID, ARG(SALARY) 平均工资 FROM WORK GROUP BY DID),
    EM AS
     (SELECT EMP.*, W.SALARY FROM EMP LEFT JOIN WORK W ON EMP.EID = W.EID)
    SELECT * FROM WD, EM WHERE WD.DID = EM.DID AND WD.平均工资 > EM.SALARY
    


  • 相关阅读:
    JavaScript学习笔记(三十八) 复制属性继承
    每天一道逻辑思维题
    动态规划(4):求子数组最大和
    30天自制操作系统第四天学习笔记
    UVA 1344 Tian Ji -- The Horse Racing
    Word隐藏回车符技巧
    Apache Thrift
    Android更改桌面应用程序launcher的两种方式
    Java语言实现简单FTP软件------>FTP软件效果图预览之下载功能(二)
    Java Collection
  • 原文地址:https://www.cnblogs.com/liuzhuqing/p/7480424.html
Copyright © 2020-2023  润新知