Oracle查询中子选择过多?

问题描述 投票:0回答:2

我想知道如何改善以下查询的性能,因为它花费的时间太长了,毕竟,它返回了数百万行。。。 ...

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)
sql oracle performance optimization querying
2个回答
0
投票

我建议先确定查询的SQL ID,然后使用SQL Monitor Report,因为它会告诉您确切的执行计划以及SQL在大部分时间都花在哪里。

从SQL * Plus获取SQL Monitor报告的简单方法如下:

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
投票

这里是最初的刺伤,可能会带来重大改善。您的许多查询都是针对每个记录执行的关联子查询。取而代之的是,我尝试在“选择自/加入”部分中为每个帐号构建查询前汇总。首先查询,然后再解释逻辑。

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表,并在PPDD_Date小于TRUNC(sysdate)(即“ DEBT”结果列)的情况下,获得每个帐户的最大Bill_Ref_No和余额的总和。因此,现在我们有了您感兴趣的帐户的有限列表,该帐户的最大账单以及所欠余额的总和。

 (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“最后一笔账单”

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

最后,子查询别名结果为“ CCK”。由于折扣和周期使用相同的查询/子查询/存在,因此只要符合折扣类型的条件并为JOIN条件提取Account_No,就可以对其进行运行。现在我们有了每个帐户的折扣和周期值。

如果您返回那么多行,我相信通过预先捕获这些预查询聚合并由该帐户加入而获得的性能将比每行每个子查询的性能要快得多。

有一个对ROWNUM的引用,但没有任何表/别名引用,所以我不确定该引用在查询中的影响。

最后注。对于折扣可能不适用的事情,您可能需要将其更改为LEFT JOIN,其中的那些值将显示为NULL。但不知道数据的范围,即给定表中1:许多项的笛卡尔乘积,我认为这将对您很好。在大多数情况下,似乎所有情况都导致每个帐户仅合格的一条记录对加入的重要性更高(例如,最高帐单参考)。

© www.soinside.com 2019 - 2024. All rights reserved.