2012-03-27 108 views
0

我在Oracle 11g中编写了以下SQL查询。如何减少Oracle中以下查询的执行时间?

SELECT p.matchcode  pmatchcode, 
     p1.matchcode  p1matchcode, 
     p.digits   digit, 
     p1.effectivedate peff, 
     p1.expirydate pexp, 
     p.expirydate  p1exp, 
     p.tariff_id  tariff_id 
    FROM tt_matchcodes_view p1 
    JOIN tt_matchcodes_view p 
    on p.tariff_id = p1.tariff_id 
    AND p.type_id = p1.type_id 
    AND p1.Digits = p.Digits 
    AND p.matchcode <> p1.matchcode 
    AND p1.EffectiveDate < p.expirydate 
    AND (p1.expirydate IS NULL OR p1.expirydate > p.expirydate) 
    AND substr(p.matchcode, 0, length(p1.matchcode)) = p1.matchcode; 

tt_matchcodes_view表有71392条记录。我在字段matchcode and digits上的表上创建了两个索引。执行时间超过10分钟。无论如何减少执行时间。

样品表中的数据:

MATCHCODE DIGITS DEST_ID MATCH EFFECTIVEDATE EXPIRYDATE INHERITED TARIFF_ID TYPE_ID 
1787 1787 73999 1 01/03/2012  0 22 1 
1787201 1787 73999 0 01/03/2012  -1 22 1 
1787202 1787 73999 0 01/03/2012  -1 22 1 
1787203 1787 73999 0 01/03/2012  -1 22 1 
1787204 1787 73999 0 01/03/2012  -1 22 1 
1787205 1787 73999 0 01/03/2012  -1 22 1 
1787206 1787 73999 0 01/03/2012  -1 22 1 
1787207 1787 73999 0 01/03/2012  -1 22 1 
1787208 1787 73999 0 01/03/2012  -1 22 1 
1787212 1787 73999 0 01/03/2012  -1 22 1 

执行计划:

OPERATION OPTIONS OBJECT_NAME OBJECT_INSTANCE OPTIMIZER ID PARENT_ID DEPTH POSITION COST CARDINALITY BYTES CPU_COST IO_COST 
SELECT STATEMENT    ALL_ROWS 0  0 703 703 3 501 83322403 698 
HASH JOIN     1 0 1 1 703 3 501 83322403 698 
TABLE ACCESS FULL TT_MATCHCODES_VIEW 2  2 1 2 1 95 65498 5174342 22711001 94 
TABLE ACCESS FULL TT_MATCHCODES_VIEW 1  3 1 2 2 95 65498 5763824 22711001 94 

Thx提前。

+0

为什么你有'substr(p.matchcode,0,length(p1.matchcode))'?这是没有意义的。同样@GauravSoni提到,请编辑并添加执行计划 – Sathya 2012-03-27 10:05:14

+0

您将'tt_matchcodes_view'描述为表,但它的名称暗示着不同的东西。那么,它真的是一张桌子,还是它实际上是一个视图? – APC 2012-03-27 10:13:47

+0

P1的匹配码应出现在P匹配码中。这就是为什么我在那里保持SUBSTR。 tt_matchcodes_view只是一个临时表,不是一个视图[忽略名称]。 – 2012-03-27 10:19:19

回答

1

由于这是一个全局临时表,因此您必须填充它,然后在同一会话甚至事务中执行查询。这让我想知道Oracle是否在空表上收集了统计数据,但没有反映其实际内容。您可能会在运行查询之前尝试收集统计信息。如果这种方法有效,并且表格的内容预计不会相差很大,那么您可能需要固定这些统计数据,以便它们不会被替换。

但是,根据您迄今为止提供的信息,我不确定Oracle可以提供更好的计划。仅在matchcode上的索引对于此查询可能不可用,因为在谓词中给定条件。可以使用digits上的索引,但由于您自己将表加入表中,因此在执行两次完整扫描时可能效率较低,因为digits上始终存在匹配项(除非它为NULL,否则)。

我们需要知道更多关于谓词中哪些条件过滤出最多行以提出更多建议的更多细节。假设matchcode上的不等式是主要过滤器,那么您可能会从(digits,matchcode)上的单个索引中获得一些好处 - 它可能会将索引与其自身结合起来,并在完成表格之前删除大量行。

2

您的表格有超过70000行。您正在选择所有记录两次并根据非等值比较行。所以基本上你会将每一行与表中的每一行进行比较。 (实际上并非全部都是这样,因为不是TARIFF_ID或TYPE_ID或DIGITS不匹配的那些,但看起来并不多)。这是〜490,000,000个比较。在这种情况下,十分钟的执行时间似乎不算太糟糕。

解释计划显示Oracle已经选择了最好的计划。所有你能做的事情就是为Oracle提供一个更有用的索引。使用where子句中所有列的复合索引可能有所帮助。事情是这样的:

create index super_match_idx on tt_matchcodes_view 
    (tariff_id, .type_id, digits, matchcode, expirydate, effectivedate)  

这可能给你的指标,这应该是更快的超过两个全表扫描操作的两个完整的快速扫描。

顺便说一句,您是否在填充临时表时排序数据?使用与索引对齐的ORDER BY可以改善聚类因子。所以你可能会得到更快的检索,因为所有匹配的行更可能在连续的块中。

我通常不会建议对堆表中的行进行排序,但是因为您已经支付了插入临时表的开销,所以您可能会获得尽可能多的果汁。

哦,substr(p.matchcode, 0, length(p1.matchcode))是一个死的赠品。智能钥匙是Teh Suck!无论如何,有没有这种情况下,SUBSTR()调用返回一个不匹配DIGITS的值? (同样你的样本数据是不明确的。)如果DIGITS可以识别SUBTSR()的输出,我建议你把最后一行丢掉。