Помогите оптимизировать SQL-запрос - PullRequest
0 голосов
/ 09 июня 2011

У меня огромный запрос, который постепенно рос с годами, и все, что я сделал, - это добавил к нему подпроцедуры, чтобы удовлетворить рост. проблема, с которой я столкнулся сейчас, заключается в том, что выполнение этого запроса занимает около 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 )

как вы можете видеть, что обе эти подпроцедуры одинаковы, единственная разница - сумма в операторе выбора. Есть ли способ, которым я могу запустить только одну из подпроцедур, чтобы найти сумму?

Я показал полный запрос, пожалуйста, кто-нибудь может мне помочь?

Ответы [ 3 ]

5 голосов
/ 09 июня 2011

конечно - как то так:

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 голос
/ 09 июня 2011

После некоторого обзора, я думаю, у меня есть кое-что для вас, чтобы сделать снимок ... Возможно, вам придется его немного откорректировать, но, надеюсь, заберет для вас МНОГО путаницы. Самым большим убийством в запросе является то, что у вас есть значения COUNT (DISTINCT), которые НЕ основаны на основных критериях квалифицированных записей ...

При этом, глядя на все ваши объединения, вы объединяетесь из таблицы записей в таблицу повторений и из представителя в таблицу контактов. Если идентификатор представителя всегда указывает на контакт, чтобы получить один и тот же «идентификатор», то кажется, что код представителя подобен ярлыку фактического «contact_id» в таблице контактов продавца. Тем не менее, нет необходимости присоединяться к таблице Rep или таблице контактов. Заголовок продаж имеет «REP_CODE», поэтому это может исключить все объединения и группировки (если вы не исправите меня иначе).

Далее. У меня есть два предварительных запроса, начиная с самых внутренних. Ваш исходный запрос OUTERMOST должен был иметь только идентификаторы записей, основанные на типе записи "SJOB" и Age = 0. Таким образом, мой "PQ1" (предварительный запрос 1) получит ТОЛЬКО те записи, которые соответствуют этому условию, и получит код представителя, идентификатор и имя контакта, а также соответствующий год / период. Это все ОТЛИЧАЕТСЯ.

Этот результат затем присоединяется к таблице записей (псевдоним e2) на основе того же представителя, года и периода (следовательно, исключается куча других соединений / где-либо еще). Затем он соединяет e2 с таблицей сведений, чтобы получить все элементы. Это начало «PQ2» (предварительный запрос 2).

Поскольку я получил разные данные для данного контактного представителя, года и периода, похоже, что вы получаете общую активность для любого человека в основном запросе PQ1 и просматриваете его ОБЩУЮ активность по продажам в течение того же квалифицированного года / периода.

Условие where для получения сумм брутто, продаж и счетов-фактур имело 3 условия Это основное предложение WHERE условия PreQuery 2. В списке полей, поскольку все они в основном имели одинаковые квалификационные критерии, я просто добавил

СУММА (случай, когда ...) как MTD_GP, сумма (случай, когда ...) как MTD_Sales, sum () для Order_GP и Order_Sales (на основе части FIRST Где) sum () для INVOICED_GP и INVOICED_Sales (на основе части SECOND Where) sum () для EXPEND (основано на ТРЕТЬЕЙ части части where)

Как только я получил это полное и все свернутое, сгруппированное по данному представителю, периоду, году, я взял результат THAT в качестве основы, чтобы завершить все ваши ДРУГИЕ записи count count (), count (*) и т. Д. Опять же, поскольку я знаю, что он уже разбит на данный Rep, я могу повторить запрос к таблице записи 2 аналогичным образом.

Надеюсь, это очень прояснит то, что я воспринимаю, как ваш запрос о попытках достичь цели, и заставит вас найти лучшее / окончательное решение.

Из ваших объединений кажется, что ваша "таблица повторений" имеет идентификатор контакта, в котором все повторы могут указывать на один "контакт". Нет необходимости явно присоединяться к контакту только для того идентификатора контакта, который уже существует в таблице повторений ... Таким образом, вы можете упростить объединение только для таблицы ввода в таблицу повторений и получить идентификатор контакта.

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
0 голосов
/ 09 июня 2011

Как правило, вы не должны использовать коррелированные подзапросы для любого запроса, где набор данных в конечном итоге будет большим, поскольку они ведут себя как курсоры и запускаются построчно.Вместо этого вы должны использовать соединения с таблицами или производными таблицами.

Это верно для SQl Server. Я знаю, что оно может отличаться в других базах данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...