我遇到了一些我无法解释的怪异事。
我正在使用以下查询:
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
子句中1}}没问题。我的问题是 - 为什么oracle不评估应该只过滤特定分区的ON
子句?
答案 0 :(得分: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
;
答案 1 :(得分: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
包含有关分区的信息,但要对已加入的数据应用where
,首先应用ON
条件。但是在那个时间点它没有关于分区的信息site_id
所以它决定扫描所有分区。您需要在第一步(即加入on
)通知Oracle您想要使用的分区。
换句话说,首先需要解析包含有关分区信息的where
:
MERGE INTO Main_Table t
USING Stg_Table s
ON(s.site_id = t.site_id)
在这里你必须访问所有分区。 where
是when matched
的一部分,因此首先我们需要确定是否存在任何匹配,而不了解分区。
答案 2 :(得分:1)
应该区分WHERE clause
合并插入/更新子句和WHERE clause
SELECT语句。
MERGE子句通常是saing - 不要更新或不要插入。一般情况下,一代ACCESS谓词并非无足轻重。
正如其他人在仅有一个UPDATE WHERE子句的最简单情况中指出的那样,从11g开始没有执行ACCESS谓词生成。
这也是文件
如果希望数据库仅在指定的条件为真时才执行更新操作,请指定where_clause。条件可以指数据源或目标表。如果条件不为真,则数据库会在将行合并到表中时跳过更新操作。
即。谓词跳过更新而不是行源中的记录。