Oracle - Оптимизация запросов - Запрос выполняется долго - PullRequest
0 голосов
/ 19 июня 2020

У меня есть запрос oracle, который выполняется один раз в месяц для обработки деталей заказа. На выполнение этого запроса уходит очень много времени. (Более тридцати минут). Поэтому я пытаюсь это оптимизировать. У меня неплохие знания в Oracle, и я объясню, что я пробовал до сих пор. Тем не менее, это займет около 20 минут. Это вопрос. Oracle версия - 11g.

SELECT store_typ, store_no, COUNT(order_no) FROM
(
    SELECT DISTINCT(order_no), store.store_no, store.store_typ FROM 
    (
        SELECT trx.order_no,trx.ADDED_DATE, odr.prod_typ, odr.store_no FROM daily_trx trx 
        LEFT OUTER JOIN 
        (
            SELECT odr.order_no,odr.prod_typ,prod.store_no FROM order_main odr 
            LEFT OUTER JOIN ORDR_PROD_TYP prod
            on odr.prod_typ = prod.prod_typ  
        ) odr
        ON trx.order_no=  odr.order_no
    ) daily_orders ,  
    (SELECT store_no,store_typ FROM main_stores ) store

    WHERE 1=1 
    and daily_orders.order_no !='NA'
    and store.store_no = daily_orders.store_no
    AND to_timestamp(to_char(daily_orders.ADDED_DATE,'DD-MM-YYYY HH24:MI:SS'),'DD-MM-YYYY HH24:MI:SS') >= to_date('01-05-2020 00:00:00','DD-MM-YYYY HH24:MI:SS')
    AND to_timestamp(to_char(daily_orders.ADDED_DATE,'DD-MM-YYYY HH24:MI:SS'),'DD-MM-YYYY HH24:MI:SS') <= to_date('31-05-2020 23:59:59','DD-MM-YYYY HH24:MI:SS')
)
GROUP BY store_typ, store_no

Фон

  • order_main - в этой таблице более 4 миллионов записей
  • Я ввел индекс для столбца order_no, который сократил время для выполнения.

Мои вопросы следующие.

1) Поможет ли я перенести валидацию даты во внутренний запрос вот так?

SELECT store_typ, store_no, COUNT(order_no) FROM
(
    SELECT DISTINCT(order_no), store.store_no, store.store_typ FROM 
    (
        SELECT trx.order_no,trx.ADDED_DATE, odr.prod_typ, odr.store_no FROM daily_trx trx 

        LEFT OUTER JOIN 
        (
            SELECT odr.order_no,odr.prod_typ,prod.store_no FROM order_main odr 
            LEFT OUTER JOIN ORDR_PROD_TYP prod
            on odr.prod_typ = prod.prod_typ  
        ) odr
        ON trx.order_no=  odr.order_no
        WHERE  to_timestamp(to_char(daily_orders.ADDED_DATE,'DD-MM-YYYY HH24:MI:SS'),'DD-MM-YYYY HH24:MI:SS') >= to_date('01-05-2020 00:00:00','DD-MM-YYYY HH24:MI:SS')
        AND to_timestamp(to_char(daily_orders.ADDED_DATE,'DD-MM-YYYY HH24:MI:SS'),'DD-MM-YYYY HH24:MI:SS') <= to_date('31-05-2020 23:59:59','DD-MM-YYYY HH24:MI:SS')
    ) daily_orders ,  
    (SELECT store_no,store_typ FROM main_stores ) store

    WHERE 1=1 
    and daily_orders.order_no !='NA'
    and store.store_no = daily_orders.store_no
    --AND to_timestamp(to_char(daily_orders.ADDED_DATE,'DD-MM-YYYY HH24:MI:SS'),'DD-MM-YYYY HH24:MI:SS') >= to_date('01-05-2020 00:00:00','DD-MM-YYYY HH24:MI:SS')
    --AND to_timestamp(to_char(daily_orders.ADDED_DATE,'DD-MM-YYYY HH24:MI:SS'),'DD-MM-YYYY HH24:MI:SS') <= to_date('31-05-2020 23:59:59','DD-MM-YYYY HH24:MI:SS')
)
GROUP BY store_typ, store_no

2) Не могли бы вы предложить какие-либо другие улучшения, которые можно внести в этот запрос?

3) Дополнительная индексация поможет в любых других таблицах / столбцах? Только таблицы daily_trx и order_main - это таблицы, содержащие огромное количество данных.

Ответы [ 2 ]

2 голосов
/ 19 июня 2020

Некоторые общие предложения

  1. Не объединяйте синтаксис ANSI и Oracle Join в одном запросе

  2. Не используйте внешнее соединение, если можно использовать внутреннее соединение

Ваши внутренние подзапросы используют внешние соединения , но окончательное соединение с main_stores - внутреннее соединение, удаляющее все строки с помощью store_no is null - вы можете использовать внутренние соединения с тем же результатом.

Ранняя фильтрация строк

Субоптимальная практика - сначала присоединиться к подзапросу, а затем отфильтровать соответствующую строку с where условиями

Использовать простые предикаты

Если вы хотите ограничить столбец DATE, сделайте это следующим образом

trx.ADDED_DATE >= to_date('01-05-2020 00:00:00','DD-MM-YYYY HH24:MI:SS') 
При необходимости используйте count distinct

Запрос select DISTINCT в третьей строке можно исключить, если вы используете COUNT(DISTINCT order_no)

Применение всех вышеупомянутый пункт я прихожу к следующему запросу

select 
  store.store_no, store.store_typ, count(DISTINCT trx.order_no) order_no_cnt
from daily_trx trx
join order_main odr on trx.order_no = odr.order_no
join ordr_prod_typ prod on odr.prod_typ = prod.prod_typ 
join main_stores store on store.store_no = prod.store_no
where trx.ADDED_DATE >=  date'2020-05-01' and
trx.ADDED_DATE < date'2020-06-01' and
trx.order_no !='NA'
group by store.store_no, store.store_typ
0 голосов
/ 20 июня 2020

Рекомендации по производительности

Вы обрабатываете данные за месяц, поэтому, вероятно, будет большое количество транзакций (скажем, 100K +). В этом случае лучшим подходом является полное сканирование двух больших таблиц и выполнение HASH JOIN s.

Вы можете ожидать этот план выполнения

    ----------------------------------------------------------------------------------------------
| Id  | Operation            | Name          | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |               |   199K|  5850K|       |   592   (2)| 00:00:08 |
|*  1 |  HASH JOIN           |               |   199K|  5850K|       |   592   (2)| 00:00:08 |
|   2 |   TABLE ACCESS FULL  | MAIN_STORES   |    26 |   104 |       |     3   (0)| 00:00:01 |
|*  3 |   HASH JOIN          |               |   199K|  5070K|       |   588   (2)| 00:00:08 |
|   4 |    TABLE ACCESS FULL | ORDR_PROD_TYP |    26 |   104 |       |     3   (0)| 00:00:01 |
|*  5 |    HASH JOIN         |               |   199K|  4290K|  1960K|   584   (1)| 00:00:08 |
|*  6 |     TABLE ACCESS FULL| ORDER_MAIN    |   100K|   782K|       |    69   (2)| 00:00:01 |
|*  7 |     TABLE ACCESS FULL| DAILY_TRX     |   200K|  2734K|       |   172   (2)| 00:00:03 |
----------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - access("STORE"."STORE_NO"="PROD"."STORE_NO")
   3 - access("ODR"."PROD_TYP"="PROD"."PROD_TYP")
   5 - access("TRX"."ORDER_NO"="ODR"."ORDER_NO")
   6 - filter("ODR"."ORDER_NO"<>'NA')
   7 - filter("TRX"."ADDED_DATE"<TO_DATE(' 2020-06-01 00:00:00', 'syyyy-mm-dd 
              hh24:mi:ss') AND "TRX"."ORDER_NO"<>'NA' AND "TRX"."ADDED_DATE">=TO_DATE(' 2020-05-01 
              00:00:00', 'syyyy-mm-dd hh24:mi:ss'))

Если вы Если есть опция разделения , вы получите огромную прибыль , определив схему ежемесячного разделения (альтернативу ежедневное разделение ) для двух таблиц DAILY_TRX и ORDER_MAIN.

Если вышеприведенное предположение неверно и у вас есть очень мало транзакций в выбранном временном интервале (скажем, менее 1 КБ) - вам будет go лучше использовать доступ к индексу и NESTED LOOPS присоединяется.

Вам понадобится этот набор индексов

create index daily_trx_date on daily_trx(ADDED_DATE);
 
create unique index order_main_idx on order_main (order_no);

create unique index ORDR_PROD_TYP_idx1 on ORDR_PROD_TYP(prod_typ);  

create unique index main_stores_idx1 on main_stores(store_no); 

Ожидаемый план следующий

---------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                  |    92 |  2760 |    80   (4)| 00:00:01 |
|*  1 |  HASH JOIN                     |                  |    92 |  2760 |    80   (4)| 00:00:01 |
|*  2 |   TABLE ACCESS BY INDEX ROWID  | DAILY_TRX        |    92 |  1288 |     4   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN            | DAILY_TRX_DATE   |    92 |       |     3   (0)| 00:00:01 |
|*  4 |   HASH JOIN                    |                  |   100K|  1564K|    75   (3)| 00:00:01 |
|   5 |    MERGE JOIN                  |                  |    26 |   208 |     6  (17)| 00:00:01 |
|   6 |     TABLE ACCESS BY INDEX ROWID| MAIN_STORES      |    26 |   104 |     2   (0)| 00:00:01 |
|   7 |      INDEX FULL SCAN           | MAIN_STORES_IDX1 |    26 |       |     1   (0)| 00:00:01 |
|*  8 |     SORT JOIN                  |                  |    26 |   104 |     4  (25)| 00:00:01 |
|   9 |      TABLE ACCESS FULL         | ORDR_PROD_TYP    |    26 |   104 |     3   (0)| 00:00:01 |
|* 10 |    TABLE ACCESS FULL           | ORDER_MAIN       |   100K|   782K|    69   (2)| 00:00:01 |
---------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - access("TRX"."ORDER_NO"="ODR"."ORDER_NO")
   2 - filter("TRX"."ORDER_NO"<>'NA')
   3 - access("TRX"."ADDED_DATE">=TO_DATE(' 2020-06-01 00:00:00', 'syyyy-mm-dd 
              hh24:mi:ss') AND "TRX"."ADDED_DATE"<TO_DATE(' 2020-07-01 00:00:00', 'syyyy-mm-dd 
              hh24:mi:ss'))
   4 - access("ODR"."PROD_TYP"="PROD"."PROD_TYP")
   8 - access("STORE"."STORE_NO"="PROD"."STORE_NO")
       filter("STORE"."STORE_NO"="PROD"."STORE_NO")
  10 - filter("ODR"."ORDER_NO"<>'NA')    

Проверьте здесь как получить план выполнения вашего запроса

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