我有一个大表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代码,并且可能保持大部分性能提升,但我想必须有一种方法可以在单个查询中完成。
提前致谢。
答案 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;