我需要用这句话更新一堆行:
UPDATE "STATISTICS" SET MEDIA = (CASE WHEN TB_STATISTICS.TYPE > 3 THEN 2 ELSE 1 END);
然而,它因ORA-30036而失败,因为UNDOTBS1没有足够的空间。我没有数据库,也无法增加用于撤消的表空间。
该表有大约3000万行,如果我每次只更新1000万行,它可以正常工作:
UPDATE "STATISTICS" SET MEDIA = (CASE WHEN TB_STATISTICS.TYPE > 3 THEN 2 ELSE 1 END) where rownum < 10000000;
但是,我无法让它更新下一千万行。我试过这个:
UPDATE "STATISTICS" SET MEDIA = (CASE WHEN TB_STATISTICS.TYPE > 3 THEN 2 ELSE 1 END) where rownum > 10000000 and rownum < 20000000;
但它总是说“0行更新”。
我已经看到,在执行SELECT时,可以通过将rownum设置为所选列之一并为其分配和别名来实现。但我不知道如何在UPDATE句子中做到这一点。
THX。
答案 0 :(得分:2)
使用ROWNUM
并不总是这种操作的可靠方法。如果您有CURSOR LOOP
列,则可以使用这样的隐式UNIQUE or PRIMARY KEY
。在这里,我每100000行执行一次COMMIT
。您可以相应地修改它
DECLARE
cnt INTEGER := 0;
BEGIN
FOR rec_cur IN
(
SELECT unique_key_col,
CASE
WHEN tb_statistics.TYPE > 3 THEN 2
ELSE 1
END AS MEDIA
FROM "STATISTICS" )
LOOP
UPDATE "STATISTICS" s
SET s.media = rec_cur.media
WHERE s.unique_key_col = rec_cur.unique_key_col;
IF cnt = 100000 THEN
COMMIT;
cnt := 0;
ELSE
cnt := cnt + 1;
END IF;
END LOOP;
COMMIT;
END;
/
答案 1 :(得分:2)
您目前的情况是矛盾的,因为价值不能同时低于1000万和超过2000万。但即使你扭转了它,它仍然不会像你期望的那样工作,因为它的方式 - 以及何时 - rownum
被设定。 From the documentation:
大于正整数的
ROWNUM
值的测试条件始终为false。例如,此查询不返回任何行:SELECT * FROM employees WHERE ROWNUM > 1;
获取的第一行被赋予
ROWNUM
1,并使条件为假。要获取的第二行现在是第一行,并且还指定了ROWNUM
为1并使条件为false。所有行随后都无法满足条件,因此不返回任何行。
使用rownum
这不是理想的选择,但如果您将语句更改为:
UPDATE "STATISTICS" SET MEDIA = (CASE WHEN TB_STATISTICS.TYPE > 3 THEN 2 ELSE 1 END)
where (MEDIA IS NULL OR MEDIA != (CASE WHEN TB_STATISTICS.TYPE > 3 THEN 2 ELSE 1 END))
and rownum < 10000000 ;
然后每次运行它时,它将排除您已经更新的行,或者(作为奖励)已经具有正确值的行,因为它们将失败where
条件;但它允许该值最初也为空。
您也可以在PL / SQL块中执行此操作,最好使用批量查询和更新,也许这样的事情是您有唯一或主键字段:
declare
type t_tab_type is table of statistics%rowtype;
l_tab t_tab_type;
cursor c is select * from statistics;
begin
open c;
loop
fetch c bulk collect into l_tab limit 10000000;
forall i in 1..l_tab.count
update statistics
set media = case when l_tab(i).tb_statistics.type > 3 then 2 else 1 end
where unique_key = l_tab(i).unique_key;
commit;
exit when c%notfound;
end loop;
close c;
end;
/
但可能是一个小于一千万的限制(因此更频繁的提交,除非你跟踪一个单独的变量来控制它),因为你不想为集合使用太多的内存。您还可以通过定义记录类型并使用它来代替%rowtype
来减少使用的内存,可能只使用唯一键,并将select *
更改为仅匹配记录类型的特定列。如果只得到一列,则为varray。
(也不确定tb_statistics.type
是否是对象类型,或者更可能是实际的表名和列名,并且错过了修改发布语句的内容。如果它是同一个表中的列,并且此计算将始终与添加新行相同,然后media
可能是虚拟列。)
最好有足够的撤销空间来在一次交易中执行操作,以提高效率和重启性;但似乎这是一次性的,所以也许不值得改变,甚至是一个临时的大型撤销表空间。