[英]Oracle Performance tuning of a query in Oracle DB
I have a query which runs for almost 2 hours without producing any result. 我有一个查询,运行了近2个小时,没有产生任何结果。 I have taken the execution plan and it looks like below .
我已经采取了执行计划,它看起来像下面。
Code 码
This code basically does pivot operation: 此代码基本上执行pivot操作:
SELECT y.COMPANYNAME as OIPACOMPANYNAME,
z.POLICYNUMBER as OIPAPOLICYNUMBER,
x.STATUSCODE as OIPASTATUSCODE,
MAX(CASE
WHEN w.FIELDTYPECODE='01' AND w.FIELDNAME='PolicyEffDate' THEN w.DATEVALUE
ELSE NULL
END ) as OIPAPOLICYEFFDATE,
x.ISSUESTATECODE as OIPAISSUESTATECODE,
NULL as OIPACURRENTSTATECODE,
MAX(CASE
WHEN w.FIELDTYPECODE='01' AND w.FIELDNAME='PolicyEndDate' THEN w.DATEVALUE
ELSE NULL
END ) as OIPAPOLICYENDDATE,
MAX(CASE
WHEN w.FIELDTYPECODE='03' AND w.FIELDNAME='IssueAge' THEN w.INTVALUE
ELSE NULL
END) as OIPAISSUEAGE,
MAX(CASE
WHEN w.FIELDTYPECODE='03' AND w.FIELDNAME='PolicyYear' THEN w.INTVALUE
ELSE NULL
END) as OIPAPOLICYYEAR,
V.PLANNAME AS OIPAPLANNAME,
MAX(CASE
WHEN w.FIELDTYPECODE='02' AND w.FIELDNAME='PolicyUniqueIdentifier' THEN w.TEXTVALUE
ELSE NULL
END) as OIPAPOLUPI,
NULL as OIPAPLANMODALFACTOR,
MAX(CASE
WHEN w.FIELDTYPECODE='02' AND w.FIELDNAME='Participating' THEN w.TEXTVALUE
ELSE NULL
END) as OIPAPARTICIPATING,
MAX(CASE
WHEN w.FIELDTYPECODE='02' AND w.FIELDNAME='Participating' THEN w.TEXTVALUE
ELSE NULL
END) as OIPAREINSURANCETYPE,
MAX(CASE
WHEN w.FIELDTYPECODE='04' AND w.FIELDNAME='BaseFaceAmount' THEN w.FLOATVALUE
ELSE NULL
END) as OIPABASEFACEAMOUNT,
MAX(CASE
WHEN w.FIELDTYPECODE='02' AND w.FIELDNAME='QualType' THEN w.TEXTVALUE
ELSE NULL
END) as OIPAQUALTYPE,
NULL as OIPATAXQUALSALESMARKET, --- field record not found hence set as null
MAX(CASE
WHEN w.FIELDTYPECODE='01' AND w.FIELDNAME='BillToDate' THEN w.DATEVALUE
ELSE NULL
END) as OIPABILLTODATE,
MAX(CASE
WHEN w.FIELDTYPECODE='01' AND w.FIELDNAME='PaidToDate' THEN w.DATEVALUE
ELSE NULL
END) as OIPAPAIDTODATE,
MAX(CASE
WHEN w.FIELDTYPECODE='02' AND w.FIELDNAME='PaymentMode' THEN w.TEXTVALUE
ELSE NULL
END ) as OIPAPAYMENTMODE,
MAX(CASE
WHEN w.FIELDTYPECODE='02' AND w.FIELDNAME='PaymentMethod' THEN w.TEXTVALUE
ELSE NULL
END) as OIPAPAYMENTMETHOD,
MAX(CASE
WHEN w.FIELDTYPECODE='03' AND w.FIELDNAME='BillingLeadDays' THEN w.INTVALUE
ELSE NULL
END) as "OIPABILLING LEAD DAYS",
MAX(CASE
WHEN w.FIELDTYPECODE='03' AND w.FIELDNAME='BankDraftDay' THEN w.INTVALUE
ELSE NULL
END) as OIPABANKDRAFTDAY,
MAX(CASE
WHEN w.FIELDTYPECODE='02' AND w.FIELDNAME='EFTDraftCode' THEN w.TEXTVALUE
ELSE NULL
END) as OIPAEFTDRAFTCODE,
MAX(CASE
WHEN w.FIELDTYPECODE='04' AND w.FIELDNAME='CurrentModalPremiumAmt' THEN w.FLOATVALUE
ELSE NULL
END) as OIPACURRENTMODALPREMIUMAMT,
MAX(CASE
WHEN w.FIELDTYPECODE='04' AND w.FIELDNAME='CurrentAnnualPremiumAmt' THEN w.FLOATVALUE
ELSE NULL
END) as OIPACURRENTANNUALPREMIUMAMT,
MAX(CASE
WHEN w.FIELDTYPECODE='04' AND w.FIELDNAME='PolicyModalPremiumAdjustment' THEN w.FLOATVALUE
ELSE NULL
END) as OIPAPOLICYMODALPREMIUMADJ,
MAX(CASE
WHEN w.FIELDTYPECODE='04' AND w.FIELDNAME='PolicyFee' THEN w.FLOATVALUE
ELSE NULL
END) as OIPAPOLICYFEE,
NULL as OIPALOANPRINCIPAL,
NULL as OIPAOUTSTANDINGLOAN,
NULL as OIPALOANINTERESTDUE,
MAX(CASE
WHEN w.FIELDTYPECODE='02' AND w.FIELDNAME='LoanInterestRate' THEN w.TEXTVALUE
ELSE NULL
END) as OIPALOANINTERESTRATE,
MAX(CASE
WHEN w.FIELDTYPECODE='02' AND w.FIELDNAME='LoanInterestType' THEN w.TEXTVALUE
ELSE NULL
END) as OIPALOANINTERESTTYPE,
MAX(CASE
WHEN w.FIELDTYPECODE='02' AND w.FIELDNAME='APLIndicator' THEN w.TEXTVALUE
ELSE NULL
END) as OIPAAPLINDICATOR,
MAX(CASE
WHEN w.FIELDTYPECODE='03' AND w.FIELDNAME='LoanBankDraftDay' THEN w.INTVALUE
ELSE NULL
END) as OIPALOANBANKDRAFTDAY,
MAX(CASE
WHEN w.FIELDTYPECODE='02' AND w.FIELDNAME='LoanPaymentMethod' THEN w.TEXTVALUE
ELSE NULL
END) as OIPALOANPAYMENTMETHOD,
MAX(CASE
WHEN w.FIELDTYPECODE='02' AND w.FIELDNAME='LoanPaymentMode' THEN w.TEXTVALUE
ELSE NULL
END) as OIPALOANPAYMENTMODE,
MAX(CASE
WHEN w.FIELDTYPECODE='04' AND w.FIELDNAME='LoanModalPaymentAmount' THEN w.FLOATVALUE
ELSE NULL
END) as OIPALOANMODALPAYMENTAMOUNT,
MAX(CASE
WHEN w.FIELDTYPECODE='03' AND w.FIELDNAME='LoanBillingLeadDays' THEN w.INTVALUE
ELSE NULL
END) as OIPALOANBILLINGLEADDAYS,
MAX(CASE
WHEN w.FIELDTYPECODE='01' AND w.FIELDNAME='LoanBillingDate' THEN w.DATEVALUE
ELSE NULL
END) as OIPALOANBILLINGDATE,
NULL as OIPAPITCASHVALUE,
NULL as OIPAPITCASHSURRVALUE,
NULL as OIPANXTMODVARCASHVALUE,
NULL as OIPAPITDIVONDEP,
NULL as OIPAPITINTONDEP,
MAX(CASE
WHEN w.FIELDTYPECODE='02' AND w.FIELDNAME='DividendOptionPrimary' THEN w.TEXTVALUE
ELSE NULL
END) as OIPADIVIDENDOPTIONPRIMARY,
MAX(CASE
WHEN w.FIELDTYPECODE='02' AND w.FIELDNAME='DividendOptionSecondary' THEN w.TEXTVALUE
ELSE NULL
END) as OIPADIVIDENDOPTIONSECONDARY,
MAX(CASE
WHEN w.FIELDTYPECODE='04' AND w.FIELDNAME='DividendToApplyToCash' THEN w.FLOATVALUE
ELSE NULL
END) as OIPADIVIDENDTOAPPLYTOCASH,
NULL as OIPADIVIDENDTOAPPLYTOACCUDIV,
MAX(CASE
WHEN w.FIELDTYPECODE='04' AND w.FIELDNAME='DividendToApplyToLoan' THEN w.FLOATVALUE
ELSE NULL
END) as OIPADIVIDENDTOAPPLYTOLOAN,
MAX(CASE
WHEN w.FIELDTYPECODE='04' AND w.FIELDNAME='DividendToApplyToOYT' THEN w.FLOATVALUE
ELSE NULL
END) as OIPADIVIDENDTOAPPLYTOOYT,
MAX(CASE
WHEN w.FIELDTYPECODE='04' AND w.FIELDNAME='DividendToApplyToPremium' THEN w.FLOATVALUE
ELSE NULL
END) as OIPADIVIDENDTOAPPLYTOPREMIUM,
MAX(CASE
WHEN w.FIELDTYPECODE='04' AND w.FIELDNAME='DividendToApplyToPUA' THEN w.FLOATVALUE
ELSE NULL
END) as OIPADIVIDENDTOAPPLYTOPUA,
MAX(CASE
WHEN w.FIELDTYPECODE='04' AND w.FIELDNAME='DividendInterestRate' THEN w.FLOATVALUE
ELSE NULL
END) as OIPADIVIDENDINTERESTRATE,
MAX(CASE
WHEN w.FIELDTYPECODE='01' AND w.FIELDNAME='DividendDeclaredDate' THEN w.DATEVALUE
ELSE NULL
END) as OIPADIVIDENDDECLAREDDATE,
NULL as OIPAPUA,
NULL as OIPAPUAPURCHASEDLASTANN,----- field record not found hence set as null
NULL as OIPAOYT,--field record not found hence set as null
NULL as OIPAEICTOAPPLYTOOYT,--- field record not found hence set as null
MAX(CASE
WHEN w.FIELDTYPECODE='04' AND w.FIELDNAME='NetCostBasis' THEN w.FLOATVALUE
ELSE NULL
END) as OIPANETCOSTBASIS,
a.SEGMENTGUID as "OIPASEGMENTGUID",
MAX(CASE
WHEN b.FIELDTYPECODE='02' AND b.FIELDNAME='SegmentFormerSystemPlanCode' THEN b.TEXTVALUE
ELSE NULL
END ) as "OIPASEGMENTFORMERSYSTEMPLAN",
c.SEGMENTNAME as "OIPASEGMENTNAME",
MAX(CASE
WHEN b.FIELDTYPECODE='02' AND b.FIELDNAME='SegmentStatusCode' THEN b.TEXTVALUE
ELSE NULL
END ) as "OIPASEGMENTSTATUSCODE",
MAX(CASE
WHEN b.FIELDTYPECODE='01' AND b.FIELDNAME='SegmentIssueDate' THEN b.DATEVALUE
ELSE NULL
END ) as "OIPASEGMENTISSUEDATE",
MAX(CASE
WHEN b.FIELDTYPECODE='01' AND b.FIELDNAME='SegmentEndDate' THEN b.DATEVALUE
ELSE NULL
END ) as "OIPASEGMENTENDDATE",
MAX(CASE
WHEN b.FIELDTYPECODE='01' AND b.FIELDNAME='SegmentTerminationDate' THEN b.DATEVALUE
ELSE NULL
END ) as OIPASEGMENTTERMINATIONDATE,
MAX(CASE
WHEN b.FIELDTYPECODE='03' AND b.FIELDNAME='SegmentIssueAge' THEN b.INTVALUE
ELSE NULL
END ) as OIPASEGMENTISSUEAGE,
MAX(CASE
WHEN b.FIELDTYPECODE='03' AND b.FIELDNAME='BaseIssueAge' THEN b.INTVALUE
ELSE NULL
END ) as OIPABASEISSUEAGE
FROM ASPOLICY x LEFT JOIN ASCOMPANY y ON (x.COMPANYGUID=y.COMPANYGUID)
INNER JOIN AUDIT_EXT_TEMP_BULK Z ON (z.COMPANYGUID=y.COMPANYGUID and x.POLICYGUID=Z.POLICYGUID )
LEFT JOIN ASPOLICYFIELD w ON (W.POLICYGUID=z.POLICYGUID)
LEFT JOIN ASPLAN v ON (v.PLANGUID=x.PLANGUID AND v.COMPANYGUID=z.COMPANYGUID) -- Plan Information
LEFT JOIN ASSEGMENT a ON (x.POLICYGUID=a.POLICYGUID) -- Segment
LEFT JOIN ASSEGMENTFIELD b ON (b.SEGMENTGUID=a.SEGMENTGUID) -- Segment
LEFT JOIN ASSEGMENTNAME c ON (c.SEGMENTNAMEGUID=a.SEGMENTNAMEGUID)
GROUP BY y.COMPANYNAME,
z.POLICYNUMBER,
x.STATUSCODE,
x.ISSUESTATECODE,
v.PLANNAME,
a.SEGMENTGUID,
c.SEGMENTNAME;
Here is the tail of the statement exposed for readability: 以下是为了便于阅读而公开的声明的尾部:
FROM ASPOLICY x LEFT JOIN ASCOMPANY y ON (x.COMPANYGUID=y.COMPANYGUID)
INNER JOIN AUDIT_EXT_TEMP_BULK Z ON (z.COMPANYGUID=y.COMPANYGUID
and x.POLICYGUID=Z.POLICYGUID )
LEFT JOIN ASPOLICYFIELD w ON (W.POLICYGUID=z.POLICYGUID)
LEFT JOIN ASPLAN v ON (v.PLANGUID=x.PLANGUID
AND v.COMPANYGUID=z.COMPANYGUID) -- Plan Information
LEFT JOIN ASSEGMENT a ON (x.POLICYGUID=a.POLICYGUID) -- Segment
LEFT JOIN ASSEGMENTFIELD b ON (b.SEGMENTGUID=a.SEGMENTGUID) -- Segment
LEFT JOIN ASSEGMENTNAME c ON (c.SEGMENTNAMEGUID=a.SEGMENTNAMEGUID)
GROUP BY y.COMPANYNAME,
z.POLICYNUMBER,
x.STATUSCODE,
x.ISSUESTATECODE,
v.PLANNAME,
a.SEGMENTGUID,
c.SEGMENTNAME;
The tables have the following indexes, where left side of the arrow represents the table and right side represents the column which have index on it. 这些表具有以下索引,其中箭头的左侧表示表,右侧表示具有索引的列。 Please note: before running the query I had updated stats on the tables by running
dbms_stats.gather_table_stats('schema','table name');
请注意:在运行查询之前,我通过运行
dbms_stats.gather_table_stats('schema','table name');
更新了表的统计信息dbms_stats.gather_table_stats('schema','table name');
AUDIT_EXT_TEMP_BULK-->COMPANYGUID,POLICYGUID);
ASPLAN-->PLANGUID
ASPOLICY-->COMPANYGUID
ASCOMPANY-->COMPANYGUID
ASPOLICYFIELD-->POLICYGUID
ASSEGMENT-->POLICYGUID
ASSEGMENTFIELD-->SEGMENTGUID
ASSEGMENTNAME-->SEGMENTNAMEGUID
"This code basically does pivot operation"
“这段代码基本上做了枢轴操作”
So what it actually does is attempt to create a coherent result set from an Entity-Attribute-Value model . 所以它实际上做的是尝试从实体 - 属性 - 值模型创建一致的结果集。 These are notoriously difficult to tune.
众所周知,这些很难调整。 EAV suits certain use-cases, such as analytics, but re-constructing a row from an atomized set of columns is very expensive.
EAV适合某些用例,例如分析,但是从一组雾化列重新构造一行是非常昂贵的。 They are also tiresome to query (as you now realise!) and also error-prone, because re-construction is a manual process - this probably isn't what you need to return for
OIPAREINSURANCETYPE
: 它们也很难查询(正如你现在意识到的那样!)并且也容易出错,因为重构是一个手动过程 - 这可能不是你需要为
OIPAREINSURANCETYPE
返回的:
MAX(CASE
WHEN w.FIELDTYPECODE='02' AND w.FIELDNAME='Participating' THEN w.TEXTVALUE
ELSE NULL
END) as OIPAPARTICIPATING,
MAX(CASE
WHEN w.FIELDTYPECODE='02' AND w.FIELDNAME='Participating' THEN w.TEXTVALUE
ELSE NULL
END) as OIPAREINSURANCETYPE,
This demonstrates the unsuitability of EAV for most of the cases where it's used. 这表明EAV在大多数情况下都不适合使用。 The schema is still fixed, it's just hard-coded into reads instead of writes.
架构仍然是固定的,它只是硬编码为读取而不是写入。
Of course, EAV is not just hard on those of us who have to query the "model": it also makes it impossible for the database to understand how the data is stored, and so generates bad execution plans. 当然,对于我们这些必须查询“模型”的人来说,EAV并不难:它还使数据库无法理解数据的存储方式,从而产生糟糕的执行计划。
I admit I'm impressed: I can't remember the last time I saw a G
in the Rows
column of an explain plan. 我承认我印象深刻:我不记得上次在解释计划的
Rows
看到G
了。 That is, your explain plan shows your query is wrestling with ~923,000,000,000 rows which the GROUP BY
will boil down to a mere ~7,879,000,000 rows . 也就是说,您的解释计划显示您的查询正在与~923,000,000,000行进行搏斗,其中
GROUP BY
将归结为仅约7,879,000,000行 。
So, given how much data you're returning how long do you think it should take? 那么,根据您返回的数据量,您认为应该花多长时间?
Perhaps that final number surprises you. 也许最后的数字让你感到惊讶。 No doubt you were expecting 7789 rows (one per policy).
毫无疑问,你期待7789行(每个政策一行)。 Instead you have a monstrous Cartesian product.
相反,你有一个可怕的笛卡尔积。 Where do all those extra rows come from?
所有这些额外的行来自哪里?
One of the issues is the join on AUDIT_EXT_TEMP_BULK
: that adds more than eight hundred rows per policy because there is no filter on that table. 其中一个问题是
AUDIT_EXT_TEMP_BULK
上的AUDIT_EXT_TEMP_BULK
:每个策略添加超过800行,因为该表上没有过滤器。 That's an expensive way to retrieve a policy number, which is presumably fixed for every policy. 这是一种检索策略编号的昂贵方法,可能对每个策略都是固定的。 If there isn't a more sensible way to get this one value (say a column on the
ASPOLICY
table) replacing that table with an in-line view should reduce the numbers quite a bit: 如果没有更合理的方法来获取这个值(比如
ASPOLICY
表上的一列),用内联视图替换该表应该会减少相当多的数字:
INNER JOIN (select distinct COMPANYGUID, POLICYGUID, POLICYNUMBER
from AUDIT_EXT_TEMP_BULK Z )
ON (z.COMPANYGUID=y.COMPANYGUID and x.POLICYGUID=Z.POLICYGUID )
Another thing is the GROUP BY
: you actually need to aggregate on just these columns: 另一件事是
GROUP BY
:你实际上只需聚合这些列:
y.COMPANYNAME as OIPACOMPANYNAME,
z.POLICYNUMBER as OIPAPOLICYNUMBER,
x.STATUSCODE as OIPASTATUSCODE
Removing the other columns might improve the explain plan quite considerably. 删除其他列可能会相当大地改进解释计划。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.