优化有标量子查询的SQL

数据库环境: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_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
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‘ )
              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,就不改了。

时间: 2024-10-05 04:43:22

优化有标量子查询的SQL的相关文章

彻底搞懂oracle的标量子查询

oracle标量子查询和自定义函数有时用起来比较方便,而且开发人员也经常使用,数据量小还无所谓,数据量大,往往存在性能问题. 以下测试帮助大家彻底搞懂标量子查询. SQL> create table a (id int,name varchar2(10)); Table created. SQL> create table b (id int,name varchar2(10)); Table created. SQL> insert into a values (1,'a1'); 1

SQL Server的优化器会缓存标量子查询结果集吗

在这篇博客"ORACLE当中自定义函数性优化浅析"中,我们介绍了通过标量子查询缓存来优化函数性能: 标量子查询缓存(scalar subquery caching)会通过缓存结果减少SQL对函数(Function)的调用次数, ORACLE会在内存中构建一个哈希表来缓存标量子查询的结果. 那么SQL Server的优化器是否也会有类似这样的功能呢? 抱着这样的疑问,动手测试了一下,准备测试环境 CREATE TABLE TEST (    ID  INT );     DECLARE

Oracle sql优化之分析函数优化标量子查询

待优化语句如下 select a.code as code, a.m_code as m_code,a.stktype as f_stype,a.e_year as e_year, b.sname as sname,a.c_date as c_date,to_char(sysdate,'YYYYMMDD') as createtime, to_char(sysdate,'YYYYMMDD') as updatetime, (select sum(valuef2) from a t where t

标量子查询

--标量子查询select e.empno, e.ename, e.sal, e.deptno,       (select d.dname from dept d where e.deptno = d.deptno)as dname  from emp e--插入一条数据insert into emp(empno,deptno) values(9999,null)--返回结果15条记录--改成left join(hash outer)select e.empno, e.ename, e.sal

oracle标量子查询

SQL> conn scott/scott Connected. SQL> create table a (id int,name varchar2(10)); Table created. SQL> create table b (id int,name varchar2(10)); Table created. SQL> insert into a values(1,'a1'); 1 row created. SQL> insert into a values(2,'a2

mysql 标量子查询和非法子查询

#where或having后面:#标量子查询(单行子查询)#列子查询(多行子查询)#行子查询(多行多列) 特点:子查询放在小括号内,一般放在条件的右侧,标量子查询一般配备单行操作符使用单行操作符:<> >= <= < >列子查询:一般搭配着多行操作符使用多行操作符:in.any.some.all #标量子查询#案例:谁的工资比ABEL高的员工信息 SELECT * FROM employees WHERE salary>( SELECT salary FROM e

优化更新语句中的标量子查询

数据库环境:SQL SERVER 2008R2 今天看到开发写的一条更新语句,第一眼是觉得这个SQL的业务有问题,再细看子查询部分,才意识到这是开发人员使的“怪招”. 这个SQL能满足业务的需要,只是开发人员在写这个SQL的时候应该不会考虑到存在性能问题.具体SQL如下: UPDATE fapply_04 SET conf_y_fee_amt = ISNULL(conf_y_fee_amt, 0) + ISNULL(( SELECT SUM(fexp_03.opr_amt) FROM fexp_

left join 改写标量子查询

数据库环境:SQL SERVER 2005 有一博彩的赔率是1:20,它有2张业务表:smuchs(投注表),lottery(开奖表). smuchs表有3个字段,分别是sno(投注号码).smuch(投注金额),stime(投注时间), lottery表有2个字段,分别是lno(开奖号码).stime(开奖时间).smuchs表和lottery表的数据如下:       要求:根据每天的投注情况和开奖号码,统计指定日期的投注金额.中奖应支付金额.盈亏金额. 1.建表,导入模拟数据 CREATE

优化系列 | DELETE子查询改写优化

0.导读 有个采用子查询的DELETE执行得非常慢,改写成SELECT后执行却很快,最后把这个子查询DELETE改写成JOIN优化过程 1.问题描述 朋友遇到一个怪事,一个用子查询的DELETE,执行效率非常低.把DELETE改成SELECT后执行起来却很快,百思不得其解. 下面就是这个用了子查询的DELETE了: [[email protected]]mydb > EXPLAIN delete from trade_info where id in ( select id from ( sel