【问题标题】:Execution plan not as expected执行计划不如预期
【发布时间】:2017-04-22 10:48:15
【问题描述】:

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

我正在使用以下查询:

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)

主表:
- 已编入索引 (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 仅扫描单个分区的答案,我已经知道将 @987654325 ON 子句中的 @ 就可以了。我的问题是 - 为什么 oracle 不评估应该只过滤特定分区的 WHERE 子句?

【问题讨论】:

  • 如果您将t.period_code = 201612 移动到ON 条件而不是WHERE,您会得到相同的计划吗?
  • Oracle 版本?
  • 11g @DuduMarkovitz
  • 还有when not matched部分吗?

标签: sql oracle performance sql-execution-plan


【解决方案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)

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

【讨论】:

  • 感谢您的回答,是的,它确实只访问了一个分区。查看我的问题的编辑,我想我误导了你..
  • @sagi 好的,我已经添加了一些解释,为什么我认为会这样。
  • 解释是完全错误的。这不是优化器的工作方式。
【解决方案2】:

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

【讨论】:

  • 感谢您的回答。如果您能找到有关此行为的文档,我将很乐意批准。
  • 没有什么我不熟悉的。不太可能找到 Oracle 的关于他们 做的优化的文档,但我认为演示很好地展示了它。对比一下,看select * from t where 1=2的执行计划
【解决方案3】:

应该区分WHERE clause of merge insert / update 子句和WHERE clauseof SELECT 语句。

MERGE 子句通常是 - 不更新不插入。一般情况下,一代 ACCESS 谓词并非易事。

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

这也是文件本身

merge_update_clause

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

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

【讨论】:

  • 我没有被甲骨文公司任命。文档中的引用不应因此受到惩罚!
猜你喜欢
  • 2015-10-25
  • 2019-04-13
  • 1970-01-01
  • 2023-01-26
  • 2012-05-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-24
相关资源
最近更新 更多