帮助优化SQL查询

时间:2011-06-09 13:02:51

标签: sql optimization select

我有一个巨大的查询,这些年来逐渐增长,所有的iv都是为了满足增长而添加子程序。我现在的问题是这个查询大约需要3分钟才能运行。有人可以帮我优化这个查询

SELECT ENTRY_1.REP_CODE,CONTACT_1.NAME,
       (select sum((d2.total_goods-d2.total_cost)*et2.sign) 
        from detail d2 
        join entry e2 on d2.entry_id=e2.entry_id 
        join entry_type et2 on et2.entry_type=e2.entry_type 
        join rep r2 on e2.rep_code=r2.rep_code 
        join contact c2 on r2.contact_id=c2.contact_id 
        where ((d2.detail_type = 'O' and d2.charged_qty<d2.qty 
        and d2.nocharge_qty=0) 
        or (d2.detail_type = 'N' and e2.entry_type in('SINV','SCRN','DREC','DSRF'))
        or (( e2.entry_type = 'SJIN' ) and ( d2.total_goods = 0 ))) 
        and c2.contact_id=contact_1.contact_id 
        and (e2.year_num=entry_1.year_num and e2.period_num=entry_1.period_num)) MTD_GP,
    -- get the month to date sales  
       (select sum((d2.total_goods)*et2.sign) 
        from detail d2 
        join entry e2 on d2.entry_id=e2.entry_id 
        join entry_type et2 on et2.entry_type=e2.entry_type 
        join rep r2 on e2.rep_code=r2.rep_code 
        join contact c2 on r2.contact_id=c2.contact_id 
        where ((d2.detail_type = 'O' and d2.charged_qty<d2.qty 
        and d2.nocharge_qty=0) 
        or (d2.detail_type = 'N' and e2.entry_type in('SINV','SCRN','DREC','DSRF'))
        or (( e2.entry_type = 'SJIN' ) and ( d2.total_goods = 0 ))) 
        and c2.contact_id=contact_1.contact_id 
        and (e2.year_num=entry_1.year_num and e2.period_num=entry_1.period_num)) MTD_SALES,
    -- 
       (select sum((d2.total_goods-d2.total_cost)*et2.sign) 
        from detail d2 
        join entry e2 on d2.entry_id=e2.entry_id 
        join entry_type et2 on et2.entry_type=e2.entry_type 
        join rep r2 on e2.rep_code=r2.rep_code 
        join contact c2 on r2.contact_id=c2.contact_id 
        where (d2.detail_type = 'O' and d2.charged_qty<d2.qty and d2.nocharge_qty=0) 
        and c2.contact_id=contact_1.contact_id 
        and (e2.year_num=entry_1.year_num and e2.period_num=entry_1.period_num)) ORD_GP,

       (select sum((d2.total_goods)*et2.sign) 
        from detail d2 
        join entry e2 on d2.entry_id=e2.entry_id 
        join entry_type et2 on et2.entry_type=e2.entry_type 
        join rep r2 on e2.rep_code=r2.rep_code 
        join contact c2 on r2.contact_id=c2.contact_id 
        where (d2.detail_type = 'O' and d2.charged_qty<d2.qty and d2.nocharge_qty=0) 
        and c2.contact_id=contact_1.contact_id 
        and (e2.year_num=entry_1.year_num and e2.period_num=entry_1.period_num)) ORD_SALE,

       (select sum((d2.total_goods-d2.total_cost)*et2.sign) 
        from detail d2 
        join entry e2 on d2.entry_id=e2.entry_id 
        join entry_type et2 on et2.entry_type=e2.entry_type 
        join rep r2 on e2.rep_code=r2.rep_code 
        join contact c2 on r2.contact_id=c2.contact_id 
        where d2.detail_type = 'N' and e2.entry_type in('SINV','SCRN','DREC','DSRF') 
        and c2.contact_id=contact_1.contact_id 
        and (e2.year_num=entry_1.year_num and e2.period_num=entry_1.period_num)) INV_GP,

       (select sum((d2.total_goods)*et2.sign) 
        from detail d2 
        join entry e2 on d2.entry_id=e2.entry_id 
        join entry_type et2 on et2.entry_type=e2.entry_type 
        join rep r2 on e2.rep_code=r2.rep_code 
        join contact c2 on r2.contact_id=c2.contact_id 
        where d2.detail_type = 'N' and e2.entry_type in('SINV','SCRN','DREC','DSRF') 
        and c2.contact_id=contact_1.contact_id 
        and (e2.year_num=entry_1.year_num and e2.period_num=entry_1.period_num)) INV_SALE,

       (select sum((d2.total_goods-d2.total_cost)*et2.sign) 
        from detail d2 
        join entry e2 on d2.entry_id=e2.entry_id 
        join entry_type et2 on et2.entry_type=e2.entry_type 
        join rep r2 on e2.rep_code=r2.rep_code 
        join contact c2 on r2.contact_id=c2.contact_id 
        where ( e2.entry_type = 'SJIN' ) and ( d2.total_goods = 0 ) 
        and c2.contact_id=contact_1.contact_id 
        and (e2.year_num=entry_1.year_num and e2.period_num=entry_1.period_num)) EXPEND,

       (select count(distinct e2.trader_id) 
        from entry e2 
        join rep r2 on e2.rep_code=r2.rep_code 
        join contact c2 on r2.contact_id=c2.contact_id 
        where e2.entry_type in('SORD','SINV','DREC') 
        and c2.contact_id=contact_1.contact_id 
        and (e2.year_num=entry_1.year_num and e2.period_num=entry_1.period_num)) MTD_PUR,

       (select count(distinct e2.our_reference) 
        from entry e2 
        join rep r2 on e2.rep_code=r2.rep_code 
        join contact c2 on r2.contact_id=c2.contact_id 
        where e2.entry_type in('SORD') and c2.contact_id=contact_1.contact_id 
        and (e2.year_num=entry_1.year_num and e2.period_num=entry_1.period_num)) MTD_ORD,

    (select sum((d1.total_goods-d1.total_cost)*et.sign) 
        from detail d1 
        join entry e1 on d1.entry_id=e1.entry_id 
        join entry_type et on et.entry_type=e1.entry_type 
        left outer join detail d2 on d2.detail_id=d1.link_detail_id 
        join rep r2 on e1.rep_code=r2.rep_code 
        join contact c2 on r2.contact_id=c2.contact_id 
        where ((d1.detail_type = 'O' and d1.charged_qty<d1.qty and d1.nocharge_qty=0 and e1.entry_type in ('SORD','SRTN') 
        and e1.our_reference not like 'AUTO%') 
        or (d1.detail_type = 'N' and e1.entry_type in('SCRN','DREC','DSRF'))
        or (( e1.entry_type = 'SJIN' ) and ( d1.total_goods = 0 ))
        or(( d1.DETAIL_TYPE = 'N' ) and ( e1.ENTRY_TYPE = 'SINV' ) and ( e1.TAXPOINT_DATE = CURRENT_DATE ) and ( d2.DETAIL_TYPE = 'A' ))) 
        and e1.taxpoint_date=current_date 
        and c2.contact_id=contact_1.contact_id) DAILY_GP,
    (select count(*) 
        from entry e2 
        join rep r2 on e2.rep_code=r2.rep_code 
        join contact c2 on r2.contact_id=c2.contact_id 
        where e2.entry_type='SORD' 
        and  e2.taxpoint_date=current_date 
        and c2.contact_id=contact_1.contact_id) NUM_ORDS,
    (select count(tn.note) 
        from trader_notes tn join trader t on tn.trader_id=t.trader_id  
        join rep r2 on t.rep_code=r2.rep_code 
        join contact c2 on r2.contact_id=c2.contact_id 
        where tn.created between current_date 
        and (current_date+1) 
        and tn.note_type in ('N','S','G') 
        and tn.note is not null 
        and c2.contact_id=contact_1.contact_id) NOTES
FROM ENTRY ENTRY_1 INNER JOIN REP REP_1 ON
     (REP_1.REP_CODE = ENTRY_1.REP_CODE)
      INNER JOIN CONTACT CONTACT_1 ON
     (CONTACT_1.CONTACT_ID = REP_1.CONTACT_ID)
WHERE ( ENTRY_1.ENTRY_TYPE = 'SJOB' )
       AND ( ENTRY_1.AGE = 0 )

你可以看到这两个子程序是相同的,唯一的区别是select语句的总和。有没有办法我只能运行其中一个子程序来查找总和?

我显示了完整查询,有人可以帮助我吗?

3 个答案:

答案 0 :(得分:5)

确定 - 这样的事情:

SELECT ENTRY_1.AGE, ENTRY_1.REP_CODE,CONTACT_1.NAME, ENTRY_1.GOODS,
           sum((d2.total_goods-d2.total_cost)*et2.sign) 
           , sum((d2.total_goods)*et2.sign) 
.
.
. CODE TAKEN OUT
.
.
    FROM 
            detail d2 
            join entry e2 on d2.entry_id=e2.entry_id 
            join entry_type et2 on et2.entry_type=e2.entry_type 
            join rep r2 on e2.rep_code=r2.rep_code 
            join contact c2 on r2.contact_id=c2.contact_id 
            where ((d2.detail_type = 'O' and d2.charged_qty<d2.qty 
            and d2.nocharge_qty=0) 
            or (d2.detail_type = 'N' and e2.entry_type in('SINV','SCRN','DREC','DSRF'))
            or (( e2.entry_type = 'SJIN' ) and ( d2.total_goods = 0 ))) 
            and c2.contact_id=contact_1.contact_id 
            and (e2.year_num=entry_1.year_num and e2.period_num=entry_1.period_num))      MTD_GP
, ENTRY ENTRY_1 INNER JOIN REP REP_1 ON
         (REP_1.REP_CODE = ENTRY_1.REP_CODE)
          INNER JOIN CONTACT CONTACT_1 ON
         (CONTACT_1.CONTACT_ID = REP_1.CONTACT_ID)
    WHERE ( ENTRY_1.ENTRY_TYPE = 'SJOB' )
           AND ( ENTRY_1.AGE = 0 )

当然,这个想法是你将连接移到下面 - 将它们从SELECT子句中取出并将它们(一次)放入FROM和WHERE子句中。 - 我做的剪切和粘贴是不对的,但是你明白了(你也没有提供完整的查询以实际用作基础)

答案 1 :(得分:1)

经过一番评论后,我觉得我有一些东西可供你试一试......你可能需要调整一些,但希望能为你带来很多困惑。查询的最大杀手是如何使COUNT(DISTINCT)值不基于合格条目的主要标准......

话虽如此,看着你所有的联接,你正在从入口表到一个rep表,从rep到一个联系表。如果代表ID总是指向联系人以获得相同的“ID”,那么看起来代码就像是销售人员联系表中实际“contact_id”的快捷方式。也就是说,无需加入Rep表或Contact表。销售标题具有“REP_CODE”,因此这可以消除所有连接和分组(除非您另行更正)。

下一步。我从内心开始有两个初步查询。您的原始OUTERMOST查询只有基于条目类型“SJOB”和Age = 0的条目ID。因此,我的“PQ1”(预查询1)将只获得符合该条件并获得代码的条目,联系人ID和姓名以及相应的年份/期间。这都是DISTINCT。

然后根据相同的rep,year和period将此结果连接到条目表(e2别名)(因此消除了一堆其他连接/其他位置)。然后将e2加入细节表以获取所有项目。这是“PQ2”(预查询2)的开始。

由于我收到了针对特定联系人代表,年份和期间的明确信息,看起来您正在为主要PQ1查询中的任何人获取整体活动,并在同一合格年份/期间内查看其总体销售活动。

您获得Gross,Sales和Invoiced金额的where子句有3个条件。这是PreQuery 2条件的主要“WHERE”子句。在字段列表中,由于它们基本上都具有相同的资格标准,所以我刚刚添加了

SUM(case When ......)为MTD_GP, 总和(情况......)作为MTD_Sales, order_GP和Order_Sales的sum()(基于FIRST Where部分) INVOICED_GP和INVOICED_Sales的sum()(基于SECOND Where部分) EXPEND的sum()(基于where部分的THIRD部分)

一旦我完成了所有内容,并按照给定的代表,期间,年份进行了汇总,我将结果作为完成所有其他计数distinct(),count(*)等条目的基础。同样,因为我知道我已经将它分解为给定的Rep,所以我可以以类似的方式再次查询条目2表。

希望这能够大大澄清我对您尝试完成的查询的看法,并启动您进入更好/最终的解决方案。

从您的联接中,您的“代表”似乎有一个联系人ID,其中多个代表都可以指向一个“联系人”。无需显式JOIN仅联系已存在于Rep表中的联系人ID ...因此,您可以简化联接到Rep表的条目表并获取联系人ID。

select
      PQ2.Rep_Code,
      PQ2.Contact_ID,
      PQ2.Name,
      PQ2.Year_Num,
      PQ2.Period_Num,
      PQ2.MTD_GP,
      PQ2.MTD_SALES,
      PQ2.ORD_GP,
      PQ2.ORD_SALE,
      PQ2.INV_GP,
      PQ2.INV_SALE,
      PQ2.EXPEND,
      ( select count(distinct e2.trader_id) 
           from entry e2
           where  e2.rep_code = PQ2.Rep_Code
              and e2.Year_Num = PQ2.Year_Num
              and e2.Period_Num = PQ2.Period_Num 
              and e2.entry_type in ('SORD','SINV','DREC') ) MTD_PUR,

      ( select count(distinct e2.trader_id) 
           from entry e2
           where  e2.rep_code = PQ2.Rep_Code
              and e2.Year_Num = PQ2.Year_Num
              and e2.Period_Num = PQ2.Period_Num 
              and e2.entry_type = 'SORD' ) MTD_ORD,

      ( select count(*)
           from entry e2 
           where e2.rep_code = PQ2.Rep_Code
             and e2.entry_type='SORD' 
             and e2.taxpoint_date = current_date ) NUM_ORDS,


      ( select count(tn.note)
           from trader_notes tn 
               join trader t on tn.trader_id = t.trader_id
                            AND t.rep_code = PQ2.Rep_Code
           where 
                  tn.created between current_date and (current_date + 1) 
              and tn.note_type in ('N','S','G') 
              and tn.note is not null ) NOTES,

      ( select 
              sum( ( d3.total_goods - d3.total_cost) * et3.sign) 
           from
              entry e3
                 join detail d3 on e3.entry_id = d3.entry_id
                    left join detail dLink on d3.link_detail_id = dLink.detail_id
                 join entry_type et3 on e3.entry_type = et3.entry_type
           where 
                  PQ2.Rep_Code = e3.Rep_Code
              AND e3.taxpoint_date = current_date 
              AND (  (     d3.detail_type = 'O' 
                       and d3.charged_qty < d3.qty 
                       and d3.nocharge_qty = 0 
                       and e3.entry_type in ('SORD','SRTN') 
                       and e3.our_reference not like 'AUTO%'
                     ) 
                  or (     d3.detail_type = 'N' 
                        and e3.entry_type in ('SCRN','DREC','DSRF')
                      )
                  or (      e3.entry_type = 'SJIN'
                        and d3.total_goods = 0
                     )
                  or (     d3.DETAIL_TYPE = 'N'
                       and e3.ENTRY_TYPE = 'SINV'
                       and e3.TAXPOINT_DATE = CURRENT_DATE
                       and dLink.DETAIL_TYPE = 'A'
                     )
                 ) 
             ) DAILY_GP

   FROM
      ( select 
              PQ1.Rep_Code,
              PQ1.Contact_ID,
              PQ1.Name,
              PQ1.Year_Num,
              PQ1.Period_Num,

              sum( (d2.Total_Goods - d2.Total_Cost ) * et2.Sign ) as MTD_GP,

              sum( d2.Total_Goods * et2.Sign ) as MTD_SALES,

              sum( case when d2.detail_type = 'O' 
                         and d2.charged_qty < d2.qty 
                         and d2.nocharge_qty = 0 
                        then (d2.total_goods - d2.total_cost) * et2.sign
                        else 0 end ) ORD_GP,

              sum( case when d2.detail_type = 'O' 
                         and d2.charged_qty < d2.qty 
                         and d2.nocharge_qty = 0 
                        then (d2.total_goods * et2.sign )
                        else 0 end ) ORD_SALE,

              sum( case when d2.detail_type = 'N' 
                         and e2.entry_type in('SINV','SCRN','DREC','DSRF')
                        then (d2.total_goods - d2.total_cost) * et2.sign
                        else 0 end ) INV_GP,

              sum( case when d2.detail_type = 'N' 
                         and e2.entry_type in('SINV','SCRN','DREC','DSRF')
                        then (d2.total_goods * et2.sign )
                        else 0 end ) INV_SALE,

              sum( case when e2.entry_type = 'SJIN'
                         and d2.total_goods = 0
                        then (d2.total_goods - d2.total_cost) * et2.sign
                        else 0 end ) EXPEND

           FROM
              ( select distinct
                      r1.rep_code,
                      r1.contact_id,
                      c1.Name,
                      e1.year_num,
                      e1.period_num
                   from
                      entry e1
                         join rep r1 ON e1.rep_code = r1.rep_code
                            join contact c1 on r1.contact_id = c1.contact_id
                   where
                          e1.entry_type = 'SJOB'
                      and e1.age = 0 ) PQ1

                JOIN entry e2 on PQ1.rep_code = e2.rep_code
                             AND PQ1.Year_Num = e2.Year_Num
                             AND PQ1.Period_Num = e2.Period_Num
                   JOIN Detail d2 on e2.Entry_ID = d2.Entry_ID
                   JOIN Entry_Type et2 on e2.entry_type = et2.entry_type
            WHERE    
                 (     d2.detail_type = 'O' 
                   and d2.charged_qty < d2.qty 
                   and d2.nocharge_qty = 0  ) 

               or (    d2.detail_type = 'N' 
                   and e2.entry_type in('SINV','SCRN','DREC','DSRF') )

               or (    e2.entry_type = 'SJIN'
                   and d2.total_goods = 0 )

           GROUP BY
              PQ1.Rep_Code,
              PQ1.Contact_ID,
              PQ1.Name,
              PQ1.Year_Num,
              PQ1.Period_Num ) as PQ2

答案 2 :(得分:0)

作为一般规则,您不应将相关子查询用于数据集最终会很大的任何查询,因为它们的行为类似于游标并逐行运行。您应该使用连接到表或派生表。

对于SQl服务器我是这么认识的,它可能在其他数据库中有所不同。