pg執行計划分析小筆記


開發同事問,為什么一個標量子查詢,放在where子句后進行大小判斷,比不放在where子句后進行判斷大小運行的更快?按道理加了一次判斷,不是應該變慢么?

把語句拿過來,看了一下兩個語句的執行計划:

語句1和執行計划1:

SELECT A.*,
       /*剩余量*/
       (A.q_basic -
       (SELECT COALESCE(SUM(d.q_basic), 0.00)
           FROM e_order d
          WHERE CAST(d.r_item AS INT) = A.ID
            AND d.req_status NOT IN ('FZ'))) AS surplus
  FROM e_order A
  LEFT JOIN e_requirement b
    ON A.requirement_no = b.requirement_no
  LEFT JOIN erp_project C
    ON b.factory = C.project_no
 WHERE 1 = 1
   AND A.status IN ('WC')
   AND (A.q_basic -
       (SELECT COALESCE(SUM(d.q_basic), 0.00)
           FROM e_order d
          WHERE CAST(d.r_item AS INT) = A.ID
            AND d.req_status NOT IN ('FZ'))) > 0.00
   AND (C.project_name LIKE CONCAT('%', 'csg18098', '%') OR
       C.project_no LIKE CONCAT('%', 'csg18098', '%'))
   AND A.requirement_no LIKE CONCAT('%', '0000004390', '%');

語句2和執行計划2:

SELECT A.*,
       /*剩余量*/
       (A.q_basic -
       (SELECT COALESCE(SUM(d.q_basic), 0.00)
           FROM e_order d
          WHERE CAST(d.r_item AS INT) = A.ID
            AND d.req_status NOT IN ('FZ'))) AS surplus
  FROM e_order A
  LEFT JOIN e_requirement b
    ON A.requirement_no = b.requirement_no
  LEFT JOIN erp_project C
    ON b.factory = C.project_no
 WHERE 1 = 1
   AND A.status IN ('WC')
   AND (C.project_name LIKE CONCAT('%', 'csg18098', '%') OR
       C.project_no LIKE CONCAT('%', 'csg18098', '%'))
   AND A.requirement_no LIKE CONCAT('%', '0000004390', '%')

從上面的執行計划看,在where之后進行大小判斷后,執行時間是662.954 ms;去掉判斷后執行時間是1549.644 ms。的確如開發所說。

現在分別來看上面的兩個執行計划。
語句1在where子句后增加判斷,表關聯的順序是((((a,d_1),b),c),d)。語句2不在where子句后加判斷的關聯順序是(((a,b),c),d)。

其實這里d_1就是表示在where子句后的表e_order。這一點,可以將語句修改一下,就可以得到驗證:

SELECT A.*,
       /*剩余量*/
       (A.q_basic -
       (SELECT COALESCE(SUM(d.q_basic), 0.00)
           FROM e_order d
          WHERE CAST(d.r_item AS INT) = A.ID
            AND d.req_status NOT IN ('FZ'))) AS surplus
  FROM e_order A
  LEFT JOIN e_requirement b
    ON A.requirement_no = b.requirement_no
  LEFT JOIN erp_project C
    ON b.factory = C.project_no
 WHERE 1 = 1
   AND A.status IN ('WC')
   AND (A.q_basic -
       (SELECT COALESCE(SUM(e.q_basic), 0.00)
           FROM e_order e
          WHERE CAST(e.r_item AS INT) = A.ID
            AND e.req_status NOT IN ('FZ'))) > 0.00
   AND (C.project_name LIKE CONCAT('%', 'csg18098', '%') OR
       C.project_no LIKE CONCAT('%', 'csg18098', '%'))
   AND A.requirement_no LIKE CONCAT('%', '0000004390', '%');

修改后,關聯的順序就是表關聯的順序是((((a,e),b),c),d)。故d_1就是表示在where子句后的表e_order。

回看執行計划1,可以看到很多關鍵字(never executed)。其實在執行計划1中,(a,d_1)兩個表關聯后,返回的行數是0,所以之后加入連接的表其實並未執行實際連接操作,即b,c,d並未真的執行join操作。這個語句執行(a,d_1)兩個表關聯后就結束了。

而在where子句后刪除對子查詢結果大小判斷后,表的連接順序是(((a,b),c),d)。從執行計划2中可以看到,每個表都參與的join操作后,整個語句才執行結束。因此,時間比第一個執行計划的時間長了。

這里,子查詢結果判斷后返回的結果是0行。如果,不是0行呢?
我們把語句1中>0.00換成=0.000000,看看執行計划:

這個執行計划3,就比執行計划1和執行計划2都慢了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM