执行计划不符合预期

Execution plan not as expected

我遇到了一些我无法解释的怪事。

我正在使用以下查询:

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                    |

编辑: 澄清一下,如果不清楚,我不是在寻找关于如何让 Oracle 只扫描单个分区的答案,我已经知道将 t.period_code = 201612 放在 ON 子句中就可以了。我的问题是 - 为什么 oracle 不计算应该只过滤特定分区的 WHERE 子句?

请考虑将 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 包含有关分区的信息,但要在连接数据上应用 where,它需要先应用 ON 条件。但是在那个时候它没有关于分区的信息 site_id 所以它决定扫描所有分区。您需要在第一步(即加入 on)通知 Oracle 您要使用哪个分区。

换句话说,应用包含分区信息的where首先需要解析:

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

在这里您必须访问所有分区。 wherewhen matched 的一部分,因此首先我们需要在不了解分区的情况下确定是否存在任何匹配项。

似乎根本没有对 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
;

WHERE clause of merge insert / update 子句和 WHERE clauseof SELECT 语句之间应该有所区别。

MERGE 子句通常表示 - 不更新不插入。一般情况下生成 ACCESS 谓词并不简单。

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

它也是这样的文献

merge_update_clause

Specify the where_clause if you want the database to execute the update operation only if the specified condition is true. The condition can refer to either the data source or the target table. If the condition is not true, then the database skips the update operation when merging the row into the table.

即谓词跳过更新而不是行源中的记录。