2016-12-07 69 views
8

我遇到了一些我不能解释的奇怪现象。执行计划与预期不符

我使用下面的查询:

MERGE INTO Main_Table t 
USING Stg_Table s 
ON(s.site_id = t.site_id) 
WHEN MATCHED THEN 
    UPDATE SET t.arpu_prev_period = s.arpu_prev_period 
       .... --50 more columns 
    where t.period_code = 201612 

Stg_Table:索引(SITE_ID)

MAIN_TABLE:
- 索引(Period_code,SITE_ID)
- 注 - 通过period_code
分区 - 我尝试在Site_Id上单独添加索引,执行计划相同。

我期待一个使用单分区扫描的执行计划,但我得到Partition list all

这是执行计划:

6 | 0 | MERGE STATEMENT  |        | 
7 | 1 | MERGE    | Main_Table     | 
8 | 2 | VIEW    |        | 
9 | 3 | HASH JOIN   |        | 
10 | 4 |  TABLE ACCESS FULL | Stg_Table      | 
11 | 5 |  PARTITION LIST ALL|        | 
12 | 6 |  TABLE ACCESS FULL| Main_Table     | 

编辑:对于澄清,如果它是不明确的,我不是在寻找如何使甲骨文的答案,只扫描单个分区,我已经知道把t.period_code = 201612放在ON条款中会没问题。我的问题是 - 为什么oracle不会评估应该只过滤特定分区的WHERE子句?

+2

如果您将't.period_code = 201612'移动到'ON'状态而不是'WHERE',您是否得到相同的计划? – Kacper

+0

Oracle版本? –

+0

11g @DuduMarkovitz – sagi

回答

3

好像没有对UPDATE的WHERE子句进行优化。

create table t (n,x) as select level n,-1 x from dual connect by level <= 1000000; 
create table s (n,x) as select level n,-1 x from dual connect by level <= 1000000; 

merge into t 
using s 
on (s.n = t.n) 
when matched then update set t.x = s.x where 1=2 
; 

enter image description here

+0

感谢您的回答。如果您能找到关于此行为的文档,我很乐意批准此文件。 – sagi

+0

没什么我熟悉的。这是不太可能找到他们**不做的优化Oracle的文档,但我认为演示很好。相比之下,看看'select * from t where 1 = 2'的执行计划。 –

1

请考虑将t.period_code = 201612移交给我们:on (t.period_code = 201612 and s.site_id = t.site_id)。我认为你的查询是试图访问所有分区PARTITION LIST ALL,这就是问题所在。

如果添加访问单个分区的条件应该会更好。

另一种选择是:

MERGE INTO (select * from Main_Table where period_code = 201612) t 
USING Stg_Table s 
ON(t.period_code = 201612 and s.site_id = t.site_id) 
WHEN MATCHED THEN 
    UPDATE SET t.arpu_prev_period = s.arpu_prev_period 
       .... --50 more columns 
    where t.period_code = 201612 

直接指向该更新只适用于一个分区。

编辑

好了,所以试图回答的问题是为什么。 Oracle无法将两个条件合并为一个。 where包含有关分区的信息,但对于需要先应用ON条件的已加入数据应用where。但在那个时候,它没有关于分区的信息,只是site_id,所以它决定扫描所有分区。您需要在第一步(即加入on)通知Oracle您要使用的分区。

换句话说申请where其中包含有关分区的信息首先需要解决:

MERGE INTO Main_Table t 
USING Stg_Table s 
ON(s.site_id = t.site_id) 

,并在这里你必须访问所有分区。 wherewhen matched的一部分,所以首先我们需要确定是否有任何匹配或没有关于分区的知识。

+0

感谢您的回答,是的,它确实只访问了一个分区。请参阅编辑我的问题,我想我误导了你.. – sagi

+0

@sagi好的我已经添加了一些解释,为什么我认为它会这样。 – Kacper

+0

解释完全错误。这不是优化器的工作方式。 –

1

应区分合并插入/更新条款的WHERE clause和SELECT语句的WHERE clause之间进行。

合并条款是一般saing - 不更新不要插入。一般情况下的一代ACCESS谓词不是微不足道的。

正如其他人在最简单的情况下指出的那样,只有一个UPDATE WHERE子句,没有执行从11g开始的ACCESS谓词。

它也documentes这样

merge_update_clause

,如果你想在数据库中执行仅当指定条件为真更新操作指定where_clause。该条件可以引用数据源或目标表。如果条件不成立,则数据库在将行合并到表中时跳过更新操作。

即谓词skipps更新不是行源中的记录。

+0

我没有与Oracle公司任命。文件引用不应该在SO上受到惩罚! –