如何在Oracle中更改更新执行计划?

时间:2011-10-14 20:12:46

标签: sql oracle

我有一个大表foo_large和一个相对较小(几十万行)的表foo_small。大表有一个主键列“id”;小的也有“id”列,它是foo_large的外键。我想更新foo_small,以便对于每一行,其col_y在foo_large的相应行中具有等于col_x的值。最直接的方式似乎是这样的:

update foo_small sm
set col_y = (
  select col_x
  from foo_large
  where id = sm.id);
然而,这是非常低效的。对于foo_small的每一行,foo_large的相应行由其主键上的索引访问。尽管foo_small与foo_large相比较小,但它仍会导致该表上的数十万个索引扫描。更好的解决方案是在内存中散列foo_small并在foo_large上执行一次(可能是并行化的)全扫描,更新遇到的foo_small的匹配行。我可以通过以下方式做到这一点:

update
(
  select /*+ ordered use_hash(lg) parallel(lg 2) */
    sm.*, lg.col_x
  from
    foo_small sm,
    foo_large lg
  where sm.id = lg.id
)
set col_y = col_x;

此查询在一分钟内完成。不幸的是,它还有另一个缺点:它要求启动此查询的用户有权不仅更新foo_small,而且还要更新foo_large,即使后一个表实际上没有更新。有没有一个解决方案来强制后一个执行计划而不更新连接?我知道我可以用批量提取/更新编写一段程序PL / SQL代码,并且可能保持大部分性能提升,但我想必须有一种方法可以在单个查询中完成。

提前致谢。

2 个答案:

答案 0 :(得分:2)

以下是Shannon建议的最终查询:

merge /*+ leading(sm) full(lg) use_hash(lg) parallel(lg 2) */
  into foo_small sm
using foo_large lg
  on (lg.id = sm.id)
when matched then
  update set sm.col_y = lg.col_x

答案 1 :(得分:0)

每次运行查询时是否有必要更新foo_small中的每一行 - 数据是否经常更改?驱动foo_large中col_x的更改的原因是 - 您是否有更新索引标志列或时间戳,因此您只需更新实际已更改的行?

update foo_small sm
   set col_y = (
select col_x
  from foo_large
 where id = sm.id)
 where last_updated>=TRUNC(SYSDATE)-1;
相关问题