開發同事問,為什么一個標量子查詢,放在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都慢了。