數據庫環境:SQL SERVER 2008R2
今天在數據庫中抓出一條比較耗費資源的SQL,只返回904條數據,居然跑了40多分鐘。SQL及對應的數據量如下圖:
SELECT saft04.cur_year , LEFT(saft04.dept_id, 4) sdept_id , saft04.vdept_id , saft04.dept_id , saft04.fee_id , saft04.vitem_id , ISNULL(saft04.fee_amt, 0) AS saft04_fee_amt , ISNULL(saft04.fee_qty, 0) AS saft04_fee_qty , ISNULL(saft04.fee_amt_flex, 0) AS saft04_fee_amt_flex , ISNULL(saft04.adj_amt, 0) AS saft04_adj_amt , ISNULL(saft04.init_amt, 0) AS saft04_init_amt , ISNULL(saft04.flex_amt, 0) AS saft04_flex_amt , ISNULL(saft04.conf_fee_amt, 0) AS saft04_conf_fee_amt , saft04.fc_app_no , saft04.zone_id , saft04.corr_id , CASE WHEN saft04.fc_app_no < '2010' THEN ( CASE WHEN saft04.flexfapp_flag = 'Y' THEN ISNULL(fee_amt, 0) + ISNULL(adj_amt, 0) - ISNULL(conf_fee_amt, 0) ELSE ISNULL(init_amt, 0) + ISNULL(flex_amt, 0) + ISNULL(adj_amt, 0) - ISNULL(conf_fee_amt, 0) END ) ELSE CASE WHEN b.fee_type2 = '01' OR b.fee_type2 = '02' THEN ISNULL(fee_amt, 0) + ISNULL(adj_amt, 0) - ISNULL(conf_fee_amt, 0) WHEN b.fee_type2 = '03' THEN ISNULL(init_amt, 0) + ISNULL(flex_amt, 0) + ISNULL(adj_amt, 0) - ISNULL(conf_fee_amt, 0) END END bal_amt , ISNULL(( SELECT SUM(b.oPR_amt) FROM v_saft04_fexp b WHERE b.fcapp_id = saft04.fc_app_no ), 0) AS qty1 , CASE WHEN b.fee_type2 = '01' OR b.fee_type2 = '03' THEN ISNULL(saft04.conf_fee_amt, 0) - ( ISNULL(( SELECT SUM(b.opr_amt) FROM v_saft04_fexp b WHERE b.fcapp_id = saft04.fc_app_no ), 0) ) WHEN b.fee_type2 = '02' THEN ISNULL(saft04.init_amt, 0) + ISNULL(saft04.flex_amt, 0) - ISNULL(( SELECT SUM(b.opr_amt) FROM v_saft04_fexp b WHERE b.fcapp_id = saft04.fc_app_no ), 0) + ISNULL(( SELECT SUM(d.opr_amt) FROM v_fadj_rd d WHERE d.fcapp_id = saft04.fc_app_no ), 0) END qty2 , c.base_data2FROM saft04 LEFT JOIN v_ctlm60 b ON b.fee_id = saft04.fee_id LEFT JOIN ctlm1000 c ON c.d_type = 'fee_type2' AND b.fee_type2 = c.base_data1WHERE 1 = 1 AND saft04.com_id = 'LQPJ' AND saft04.cur_year = 2015 AND saft04.dept_id LIKE '2001%' AND ( saft04.dept_id IN ( SELECT dept_id FROM ctlm2000 WHERE user_id1 = '0100030' ) OR '0100030' = 'MANAGER' )ORDER BY saft04.cur_year , saft04.vdept_id , saft04.dept_id , saft04.fee_id , saft04.vitem_id , saft04.zone_id , saft04.corr_id , saft04.fc_app_no-------------------------數據量統計----------------------------------SELECT COUNT(*)FROM saft04WHERE 1 = 1 AND saft04.com_id = 'LQPJ' AND saft04.cur_year = 2015 AND saft04.dept_id LIKE '%2001%' AND ( saft04.dept_id IN ( SELECT dept_id FROM ctlm2000 WHERE user_id1 = '0100030' ) OR '0100030' = 'MANAGER' )--904SELECT COUNT(*) FROM v_saft04_fexp --1262584SELECT COUNT(*) FROM v_fadj_rd d --37077SELECT COUNT(*) FROM v_ctlm60 --431SELECT COUNT(*) FROM ctlm1000 --377
看了一下SQL,有可能出現問題的地方有2個地方,第一個是saft04 表的過濾條件“saft04.dept_id LIKE '%2001%'”使用了模糊查詢,導致
走不了既定的索引。經和業務員確定,最開始只是想查詢以“2001”開頭的單位,因此,這個條件改成“saft04.dept_id LIKE '2001%'”即可。
第二個問題,是最要命的,標量部分“SELECT SUM(b.opr_amt) FROM v_saft04_fexp b WHERE b.fcapp_id = saft04.fc_app_no”走
的執行計劃是嵌套循環,因而要改成左聯接。
改寫后的SQL如下,只執行了23S就全部出結果了。
WITH x0 AS ( SELECT b.fcapp_id , SUM(b.opr_amt) opr_amt FROM v_saft04_fexp b GROUP BY b.fcapp_id ) SELECT saft04.cur_year , LEFT(saft04.dept_id, 4) sdept_id , saft04.vdept_id , saft04.dept_id , saft04.fee_id , saft04.vitem_id , ISNULL(saft04.fee_amt, 0) AS saft04_fee_amt , ISNULL(saft04.fee_qty, 0) AS saft04_fee_qty , ISNULL(saft04.fee_amt_flex, 0) AS saft04_fee_amt_flex , ISNULL(saft04.adj_amt, 0) AS saft04_adj_amt , ISNULL(saft04.init_amt, 0) AS saft04_init_amt , ISNULL(saft04.flex_amt, 0) AS saft04_flex_amt , ISNULL(saft04.conf_fee_amt, 0) AS saft04_conf_fee_amt , saft04.fc_app_no , saft04.zone_id , saft04.corr_id , CASE WHEN saft04.fc_app_no < '2010' THEN ( CASE WHEN saft04.flexfapp_flag = 'Y' THEN ISNULL(fee_amt, 0) + ISNULL(adj_amt, 0) - ISNULL(conf_fee_amt, 0) ELSE ISNULL(init_amt, 0) + ISNULL(flex_amt, 0) + ISNULL(adj_amt, 0) - ISNULL(conf_fee_amt, 0) END ) ELSE CASE WHEN b.fee_type2 = '01' OR b.fee_type2 = '02' THEN ISNULL(fee_amt, 0) + ISNULL(adj_amt, 0) - ISNULL(conf_fee_amt, 0) WHEN b.fee_type2 = '03' THEN ISNULL(init_amt, 0) + ISNULL(flex_amt, 0) + ISNULL(adj_amt, 0) - ISNULL(conf_fee_amt, 0) END END bal_amt , ISNULL(( x0.opr_amt ), 0) AS qty1 , CASE WHEN b.fee_type2 = '01' OR b.fee_type2 = '03' THEN ISNULL(saft04.conf_fee_amt, 0) - ( ISNULL(( x0.opr_amt ), 0) ) WHEN b.fee_type2 = '02' THEN ISNULL(saft04.init_amt, 0) + ISNULL(saft04.flex_amt, 0) - ISNULL(( x0.opr_amt ), 0) + ISNULL(( SELECT SUM(d.opr_amt) FROM v_fadj_rd d WHERE d.fcapp_id = saft04.fc_app_no ), 0) END qty2 , c.base_data2 FROM saft04 LEFT JOIN v_ctlm60 b ON b.fee_id = saft04.fee_id LEFT JOIN ctlm1000 c ON c.d_type = 'fee_type2' AND b.fee_type2 = c.base_data1 LEFT JOIN x0 ON x0.fcapp_id = saft04.fc_app_no WHERE 1 = 1 AND saft04.com_id = 'LQPJ' AND saft04.cur_year = 2015 AND saft04.dept_id LIKE '2001%' AND saft04.dept_id IN ( SELECT dept_id FROM ctlm2000 WHERE user_id1 = '0100030' ) ORDER BY saft04.cur_year , saft04.vdept_id , saft04.dept_id , saft04.fee_id , saft04.vitem_id , saft04.zone_id , saft04.corr_id , saft04.fc_app_no
改寫后的SQL還有一個標量子查詢沒處理,改寫的思路和上面一樣,因執行時間已經縮短到23S,就不改了。
新聞熱點
疑難解答