Слишком много вложенных элементов в Oracle Query? - PullRequest
0 голосов
/ 24 февраля 2020

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

SELECT CIAM.EXTERNAL_ID, 
       (SELECT NEW_CHARGES / 100 
        FROM   BI_OWNER.CMF_BALANCE 
        WHERE  ( ACCOUNT_NO, BILL_REF_NO ) = (SELECT ACCOUNT_NO, 
                                                     MAX(BILL_REF_NO) 
                                              FROM   BI_OWNER.CMF_BALANCE 
                                              WHERE 
               ACCOUNT_NO = CIAM.ACCOUNT_NO 
                                              GROUP  BY ACCOUNT_NO)) 
       "AMOUNT LAST BILL", 
       (SELECT 'ACTIVE DISCOUNT' 
               || ' ' 
               || CCK.AVAIL_PERIODS 
               || '/' 
               || CC.TOTAL_PERIODS 
        FROM   BI_OWNER.CUSTOMER_CONTRACT_KEY CCK, 
               BI_OWNER.CUSTOMER_CONTRACT CC 
        WHERE  CC.PARENT_ACCOUNT_NO = CIAM.ACCOUNT_NO 
               AND CC.END_DT IS NULL 
               AND EXISTS (SELECT 1 
                           FROM   CONTRACT_TYPES 
                           WHERE  CONTRACT_TYPE = CC.CONTRACT_TYPE 
                                  AND PLAN_ID_DISCOUNT IS NOT NULL 
                                  AND DURATION_UNITS = -3) 
               AND ROWNUM = 1 
               AND CCK.TRACKING_ID = CC.TRACKING_ID 
               AND CCK.TRACKING_ID_SERV = CC.TRACKING_ID_SERV) "DISCOUNT", 
       (SELECT CC.TOTAL_PERIODS 
        FROM   BI_OWNER.CUSTOMER_CONTRACT_KEY CCK, 
               BI_OWNER.CUSTOMER_CONTRACT CC 
        WHERE  CC.PARENT_ACCOUNT_NO = CIAM.ACCOUNT_NO 
               AND CC.END_DT IS NULL 
               AND EXISTS (SELECT 1 
                           FROM   CONTRACT_TYPES 
                           WHERE  CONTRACT_TYPE = CC.CONTRACT_TYPE 
                                  AND PLAN_ID_DISCOUNT IS NOT NULL 
                                  AND DURATION_UNITS = -3) 
               AND ROWNUM = 1 
               AND CCK.TRACKING_ID = CC.TRACKING_ID 
               AND CCK.TRACKING_ID_SERV = CC.TRACKING_ID_SERV) "CYCLE"
       , 
       (SELECT SUM(BALANCE_DUE) 
        FROM   BI_OWNER.CMF_BALANCE 
        WHERE  ACCOUNT_NO = CIAM.ACCOUNT_NO 
               AND PPDD_DATE < TRUNC(SYSDATE)) 
       "DEBT" 
FROM   BI_OWNER.CUSTOMER_ID_ACCT_MAP CIAM 
WHERE  EXTERNAL_ID_TYPE = 1 
       AND EXISTS (SELECT 1 
                   FROM   BI_OWNER.CMF 
                   WHERE  ACCOUNT_NO = CIAM.ACCOUNT_NO 
                          AND PREV_CUTOFF_DATE > SYSDATE - 30)

Ответы [ 2 ]

2 голосов
/ 24 февраля 2020

Я бы порекомендовал указать идентификатор SQL для запроса, а затем использовать SQL Отчет монитора, поскольку он точно скажет вам, что такое план выполнения и где SQL тратит большую часть своего времени.

Простой способ получить отчет о мониторе SQL от SQL* Plus:

spool c:\temp\SQL_Monitor_rpt.html

SET LONG 1000000
SET LONGCHUNKSIZE 1000000
SET LINESIZE 1000
SET PAGESIZE 0
SET TRIM ON
SET TRIMSPOOL ON
SET ECHO OFF
SET FEEDBACK OFF

alter session set "_with_subquery" = optimizer;

SELECT DBMS_SQLTUNE.report_sql_monitor(
  sql_id       => '&SQLID' ,
  type         => 'HTML',
  report_level => 'ALL') AS report
FROM dual;

spool off

По сути, вам нужно знать размеры таблиц и как получить большие таблицы, чтобы иметь данные доступ через индекс (например, индекс по столбцам, найденным в предложении where).

0 голосов
/ 25 февраля 2020

Вот начальный удар и может дать значительное улучшение. Многие из ваших запросов были коррелированными подзапросами, выполняемыми для каждой записи. Вместо этого я попытался построить агрегаты перед запросом по номеру учетной записи в разделе выбора из / присоединения. Сначала запрос, затем я объясню логи c после.

SELECT 
      CIAM.EXTERNAL_ID,
      CMF_BALANCE.New_Charges / 100.0 "AMOUNT LAST BILL", 
      CCKs.Discount,
      CCKs.Cycle,
      AcntLast30.SumBalance "DEBT" 
   FROM
      (SELECT 
            CMF.Account_No,
            max( Bal.Bill_Ref_No ) MaxBillRef,
            sum( case when Bal.PPDD_Date < TRUNC(SYSDATE )
                     then Bal.Balance_Due else 0 end ) SumBalance
         from 
            BI_OWNER.CMF
               JOIN BI_OWNER.CMF_BALANCE BAL
                  on CMF.Account_No = Bal.Account_No
         where 
            CMF.PREV_CUTOFF_DATE > SYSDATE - 30
         group by
            CMF.Account_No ) AcntLast30

         JOIN BI_OWNER.CUSTOMER_ID_ACCT_MAP CIAM 
            on AcntLast30.Account_No = CIAM.Account_No 
            AND CIAM.EXTERNAL_ID_TYPE = 1 

         JOIN BI_OWNER.CMF_BALANCE
            on AcntLast30.Account_No = CMFBalance.Account_No 
            AND AcntLast30.MaxBillRef = CMFBalance.Bill_Ref_No

         JOIN
         (select
               CC.Parent_Account_No,
              CC.TOTAL_PERIODS "CYCLE",
               'ACTIVE DISCOUNT' || ' ' || CCK.AVAIL_PERIODS || '/' || CC.TOTAL_PERIODS "DISCOUNT"
            FROM
              BI_OWNER.CUSTOMER_CONTRACT CC 
                  JOIN BI_OWNER.CUSTOMER_CONTRACT_KEY CCK
                    ON CC.TRACKING_ID = CCK.TRACKING_ID
                    AND CC.TRACKING_ID_SERV = CCK.TRACKING_ID_SERV
                    AND ROWNUM = 1 
                 JOIN ( select distinct Contract_Type
                            FROM CONTRACT_TYPES
                            WHERE PLAN_ID_DISCOUNT IS NOT NULL 
                              AND DURATION_UNITS = -3) CT
                     on CC.Contract_Type = CT.Contract_Type
            WHERE
                   CC.END_DT IS NULL ) CCKs
            on AcntLast30.Account_No = CCKs.Parent_Account_No

В первоначальном предложении "FROM" у меня есть подзапрос, потому что вы, похоже, интересуетесь только учетными записями в течение последних 30 дней. Итак, пока я там, я присоединяюсь к вашей таблице CMF_Balance и получаю максимальное значение Bill_Ref_No для аккаунта И сумму баланса, когда PPDD_Date меньше TRUN C (sysdate), который является вашим столбцом результата "DEBT" , Итак, теперь у нас есть конечный список интересующих вас учетных записей с указанным счетом, максимальным счетом в файле и суммой задолженности.

 (SELECT 
                CMF.Account_No,
                max( Bal.Bill_Ref_No ) MaxBillRef,
                sum( case when Bal.PPDD_Date < TRUNC(SYSDATE )
                         then Bal.Balance_Due else 0 end ) SumBalance
             from 
                BI_OWNER.CMF
                   JOIN BI_OWNER.CMF_BALANCE BAL
                      on CMF.Account_No = Bal.Account_No
             where 
                CMF.PREV_CUTOFF_DATE > SYSDATE - 30
             group by
                CMF.Account_No ) AcntLast30

Далее, просто присоединитесь к таблице CIAM, чтобы получить только счета для External_ID_Type = 1. Это тоже можно было бы объединить с приведенным выше запросом для результата псевдонима «AcntLast30».

     JOIN BI_OWNER.CUSTOMER_ID_ACCT_MAP CIAM 
        on AcntLast30.Account_No = CIAM.Account_No 
        AND CIAM.EXTERNAL_ID_TYPE = 1 

Теперь, поскольку запрос «AcntLast30» имеет учетную запись и ссылку на максимальный счет, к которой мы затем присоединяемся CMF_Balance для счета и ссылки # на счет, таким образом, дает нам CMF_BALANCE.New_Charges / 100.0 "AMOUNT LAST BILL"

JOIN BI_OWNER.CMF_BALANCE
    on AcntLast30.Account_No = CMFBalance.Account_No 
   AND AcntLast30.MaxBillRef = CMFBalance.Bill_Ref_No

Наконец, псевдоним подзапроса приводит к "CCKs". Поскольку Discount и Cycle используют один и тот же запрос / подзапрос / существует, я просто запустил его один раз, который квалифицировал типы скидок, и извлек Account_No для условия JOIN. Теперь у нас есть значения Discount и Cycle для каждой учетной записи.

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

Была ссылка на ROWNUM без какой-либо ссылки на таблицу / псевдоним, поэтому я не уверен, что это влияние в запросе.

Конечная нота Для таких вещей, как скидка, которая может быть неприменима, вам может потребоваться изменить ее на LEFT JOIN, для которой эти значения будут показаны как NULL. Но, не зная объема данных, декартовых произведений 1: много записей в данных таблицах, я думаю, это будет хорошо работать для вас. По большей части это выглядело так, как будто все привело к тому, что только одна запись была квалифицирована для каждой учетной записи, где важнее было объединение (например, ссылка на максимальный счет).

...