Справка по оптимизации запросов Oracle - Multi Pass - PullRequest
0 голосов
/ 05 августа 2010

Мне нужно запросить таблицу заказов, чтобы получить количество всех заказов для вчерашних транзакций, сгруппированных по дате отгрузки.Затем мне нужно иметь дополнительный столбец, чтобы указать общее количество заказов на дату отгрузки для всех транзакций.Когда я добавил этот второй столбец, время обработки выросло экспоненциально (что и ожидалось) до 109 с.Есть ли способ улучшить этот SQL?Я просто хочу знать, упускаю ли я здесь что-нибудь фундаментальное.

SELECT t.shipping_date
       , t.net_orders
       , count(*) as total_orders
FROM (
    SELECT   s.store_num
             , s.store_cd
             , to_char(o.shipping_date, 'MM/DD/YYYY') as shipping_date
             , COUNT (*) as net_orders
    FROM order o left 
           join store s 
                on ( s.store_num = o.store_num )
    WHERE TRUNC (o.order_date) = TRUNC (SYSDATE - 1)
    AND s.store_cd = 'ZZZ'
    AND o.status in ('A', 'B')
    GROUP BY s.store_num
             , s.store_cd
             , to_char(shipping_date, 'MM/DD/YYYY')
) t
LEFT JOIN order o ON 
          ( TRUNC (o.shipping_date) = to_date(t.shipping_date, 'MM/DD/YYYY') 
            and o.store_num = t.store_num )
WHERE o.status in ('A', 'B')
GROUP BY t.shipping_date, t.net_orders;

У меня есть индексы для всех этих столбцов в дополнение к следующим выражениям: TRUNC (order_date) и TRUNC (shipping_date).

Ответы [ 2 ]

3 голосов
/ 05 августа 2010

Если вы просто ищете вывод, например:

shipping_date      net_orders     total_orders
01-AUG-2004        14             37
02-AUG-2004        17             29
03-AUG-2004        19             43

как насчет просто:

SELECT *
  FROM (
    SELECT TRUNC(o.shipping_date) as shipping_date
         , COUNT(CASE WHEN TRUNC(o.order_date) = TRUNC(SYSDATE - 1) 
                      THEN 1 
                      ELSE NULL 
                  END) as net_orders -- count does not count NULL values.
         , COUNT(*) as total_orders
      FROM order o 
           LEFT JOIN 
           store s 
              on s.store_num = o.store_num
     WHERE s.store_cd = 'ZZZ'
       AND o.status in ('A', 'B')
     GROUP BY TRUNC(o.shipping_date)
   ) 
WHERE net_orders > 0 -- only shipping dates that had at least one order yesterday

Это позволит избежать дополнительного объединения обратно в таблицу заказовВам в любом случае придется прикасаться ко всем заказам магазина, пока вы получаете общий счет, вы также можете одновременно делать то, что я бы назвал условным счетом.

1 голос
/ 05 августа 2010

Я переписал ваш запрос как:

   SELECT t.shipping_date, 
          t.net_orders, 
          COUNT(*) as total_orders 
     FROM (SELECT s.store_num, 
                  s.store_cd, 
                  o.status,
                  TRUNC(o.shipping_date) AS shipping_date, 
                  COUNT (*) as net_orders
             FROM STORE s
             JOIN ORDER o ON o.store_num = s.store_num
                         AND o.status IN ('A', 'B')
            WHERE s.store_cd = 'ZZZ'
              AND TRUNC(order_date) = TRUNC (SYSDATE - 1)
  GROUP BY s.store_num, s.store_cd, TRUNC(shipping_date)) t
LEFT JOIN ORDER o ON TRUNC(o.shipping_date) = t.shipping_date
                 AND o.store_num = t.store_num
                 AND o.status = t.status
 GROUP BY t.shipping_date, t.net_orders;

Некоторая незначительная перестановка, но я избавился от TO_CHAR (shipping_date), который позже конвертируется обратно в DATE.TRUNC(shipping_date) - это то же самое, что упрощает операцию.

Объединение критериев с использованием функций не будет использовать индекс - вам нужно будет создать индекс на основе функции, соответствующий критериям JOIN.

...