我想知道如何改善以下查询的性能,因为它花费的时间太长了,毕竟,它返回了数百万行。。。 ...
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 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子句中找到的列的索引)来访问大型表。
这里是最初的刺伤,可能会带来重大改善。您的许多查询都是针对每个记录执行的关联子查询。取而代之的是,我尝试在“选择自/加入”部分中为每个帐号构建查询前汇总。首先查询,然后再解释逻辑。
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:许多项的笛卡尔乘积,我认为这将对您很好。在大多数情况下,似乎所有情况都导致每个帐户仅合格的一条记录对加入的重要性更高(例如,最高帐单参考)。