我想用另一个表中的数据更新表。我目前的方法如下:
UPDATE items t1
SET (name, manufacturer_id, price) =
(SELECT
t2.item_name,
t2.item_manufacturer_id,
t2.item_price
FROM staged_items t2
WHERE t2.upgrade_version = 1234
AND t2.operation = 'modification'
AND t1.id = t2.item_id)
WHERE EXISTS (
SELECT 1
FROM staged_items t2
WHERE t2.upgrade_version = 1234
AND t2.operation = 'modification'
AND t1.id = t2.item_id)
问题在于它花了太长时间,我不知道如何获得反馈或估计需要多长时间。我一直在等待大约3个小时没有结果,并且v$session_longops
上没有待处理的操作。
我想知道如何提高更新的性能,或者另一种更有效的方法。此外,我想知道如何检查正在运行的查询的状态。
staged_items
表预计至少包含 3亿条目,并且从长远来看会增长到数十亿条目。
t2.upgrade_version = somenumber
时,预计只有“少数条目”(从0到100万)与条件(t2.operation = 'modification'
和UPDATE
)匹配。staged_items
个表,这些表都符合条件。 items
表预计会有大约 2000万个参赛作品,并且从长远来看会保持这个数量级。
UPDATE
匹配。目前我没有索引,但我正考虑在items.id
,staged_items.item_id
,staged_items.upgrade_version
和staged_items.operation
预计UPDATE
不会修改任何索引列,无论如何这可能会在将来发生变化,因此我会评论该场景将如何对建议的解决方案产生影响。
编辑最后,我选择使用rownum将查询分页到多个查询,而不是检查正在运行的查询的状态。这允许我等待每个(较小的)查询并检查并估计完整性的百分比。
考虑到这一点,我的原始查询看起来像这样:
UPDATE items t1
SET (name, manufacturer_id, price) =
(
SELECT
t2.item_name,
t2.item_manufacturer_id,
t2.item_price
FROM
(
SELECT /*+ FIRST_ROWS(n) */
a.*,
ROWNUM rnum
FROM
(
SELECT *
FROM staged_items t2
WHERE t2.upgrade_version = 1234
AND t2.operation = 'modification'
AND t1.id = t2.item_id
ORDER BY t2.id
) a
WHERE ROWNUM <= MAX_ROW_TO_FETCH
)
WHERE rnum >= :MIN_ROW_TO_FETCH
)
WHERE EXISTS (
SELECT 1
FROM staged_items t2
WHERE t2.upgrade_version = 1234
AND t2.operation = 'modification'
AND t1.id = t2.item_id)
(基于this link的对ROWNUM的分页部分)
无论如何,对于外WHERE
,我使用了Gordon Linoff's solution。
答案 0 :(得分:2)
对于您的查询,您需要一个索引:
UICollectionView
我也在考虑你可以将外部staged_items(item_id, upgrade_version, operation)
子句重写为:
where
然后,您需要WHERE t1.id IN (SELECT t2.item_id
FROM staged_items t2
WHERE t2.upgrade_version = 1234 AND t2.operation = 'modification'
)
和staged_items(upgrade_version, operation, item_id)
上的索引。请注意,索引中键的顺序很重要,您仍然希望相关子查询的第一个索引获取值。
答案 1 :(得分:1)
对于一个走进医生办公室的男人来说,这是一个老笑话。他挥动他的手臂,然后说,#Doc;当我这样做时,它很疼!&#34;。博士看着他,然后说,“那么,那就不要那样做了!&#34;
我认为你的主要问题是你的临时表很大,但你真的只需要查看一小部分数据。不要使用完整的临时表进行更新。也许您可以尝试创建在运行更新之前刷新完成的物化视图。您的席子视图将基于:
SELECT
t2.item_name,
t2.item_manufacturer_id,
t2.item_price
FROM staged_items t2
WHERE t2.upgrade_version = 1234
AND t2.operation = 'modification'
您也可以为此添加并行提示。如果每次运行更新时都需要更改这些值,也可以通过CTAS创建常规表(create table as select),使用相同的SQL但具有不同的值(upgrade_version = 5678或其他)。
您的另一个问题是跟踪。最干净的方法是在pl / sql中。它可能不像单个更新语句那么简单,但您可以添加日志记录并控制您的提交点(您的DBA会对此表示感谢)。
您的驾驶表将是垫视图(或CTAS表)。类似的东西:
declare
cursor sel_stage_mv is
select * from my_stage_mv;
l_cnt pls_integer := 0;
l_upd_cnt pls_integer := 0;
begin
for rec in sel_stage_mv
loop
l_cnt := l_cnt + 1;
-- all needed indexes are on main table (id, etc...)
update main_table
set ...
where id = rec.id;
l_upd_cnt := l_upd_cnt + SQL%ROWCOUNT;
if (mod(l_cnt, 10000) = 0) then
-- insert to some log table via autonomous procedure
ins_log(...l_upd_cnt ...);
commit;
end if;
end loop;
commit;
end;
当然,所有这些都要运行。我还对您的环境和交易要求做了一些假设,但只有您知道什么对您的设置和需求有用。
答案 2 :(得分:0)
如果您需要在表格中执行大量DML操作,则可以使用BULK COLLECT和FORALL。为了获得操作状态,我通常会创建一个日志表来存储信息。 而且,最重要的是,您可以添加一些索引来加速查询。
我不会使用MERGE语句,因为它非常慢。
所以,一个可能的解决方案是这样的:
create table log_load(table_name varchar2(50), create_date date, message varchar2(500));
declare
cursor cur_upd is
SELECT rowid as row_id
FROM items t1
WHERE EXISTS (
SELECT 1
FROM staged_items t2
WHERE t2.upgrade_version = 1234
AND t2.operation = 'modification'
AND t1.id = t2.item_id);
TYPE fetch_array IS TABLE OF cur_upd%ROWTYPE;
s_array fetch_array;
BEGIN
insert into log_load values ('item',sysdate,'Start')
commit;
OPEN cur_upd;
upd_ := 0;
LOOP
FETCH cur_upd BULK COLLECT INTO s_array LIMIT 50000;
upd_ := upd_ + s_array.COUNT;
FORALL i IN 1..s_array.COUNT
UPDATE items t1
SET (name, manufacturer_id, price) =
(SELECT t2.item_name, t2.item_manufacturer_id, t2.item_price
FROM staged_items t2
WHERE t2.upgrade_version = 1234
AND t2.operation = 'modification'
AND t1.id = t2.item_id)
WHERE t1.rowid = cur_upd[i].row_id;
insert into log_load values ('item',sysdate,'50000 updated.')
commit;
EXIT WHEN cur_upd%NOTFOUND;
END LOOP;
CLOSE cur_upd;
insert into log_load values ('item',sysdate,'end');
commit;
END;
答案 3 :(得分:0)
也许试试这个:
UPDATE
(SELECT t1.name, t1.manufacturer_id, t1.price,
t2.item_name,
t2.item_manufacturer_id,
t2.item_price,
t1.id, t2.item_id
from items t1
JOIN staged_items t2 on t1.id = t2.item_id
WHERE t2.upgrade_version = 1234
AND t2.operation = 'modification')
SET name = item_name,
manufacturer_id = item_manufacturer_id,
price = item_price;
它应该避免staged_items的双重SELECT,因此可以更快。 但是,我没有测试它。