我需要更新一个非唯一字段。我有一张桌子:
create table tbl (A number(5));
tbl中的值:1,2,2,2 .. 2.
我需要用新的非唯一值替换所有2
新值:1,100,101,102,103 .. 我写道:
DECLARE
sql_stmt VARCHAR2(500);
cursor curs is
select A from tbl group by A having count(*)>1;
l_row curs%ROWTYPE;
i number(5);
new_mail VARCHAR2(20);
BEGIN
i:=100;
open curs;
loop
fetch curs into l_row;
exit when curs%notfound;
SQL_STMT := 'update tbl set a='||i||' where a='||l_row.A;
i:=i+1;
EXECUTE IMMEDIATE sql_stmt;
end loop;
close curs;
END;
/
但我得到了:
A
----------
1
100
...
100
有什么不对?为什么循环不起作用?
答案 0 :(得分:5)
怎么样
update tbl
set a = 100 + rownum
where a in (
select a
from tbl
group by a
having count(*) > 1 )
子查询找到重复的A字段,并且更新为它们提供从100开始的唯一标识符。(这里有其他问题,例如,如果id 100,101 ....已经存在)。
PLSQL的第一条规则说你可以用SQL做什么总是用SQL 。直接写for
loop
导致在sql和pl / sql引擎之间进行大量的上下文切换。即使oracle自动将其转换为批量语句(10g<),纯SQL仍然会更快。
答案 1 :(得分:1)
每个A
的唯一值,您的光标会获得一行:
select A from tbl group by A having count(*)>1;
您需要获取与这些值匹配的所有不同行。一种方法是这样做:
select a, r from (
select a, rowid as r, count(*) over (partition by a) as c
from tbl
) where c > 1;
...然后使用rowid
值进行更新。我不确定你为什么使用动态SQL,因为它根本不是必需的,你可以简化(IMO)循环:
declare
i number(5);
begin
i:=100;
for l_row in (
select a, r from (
select a, rowid as r, count(*) over (partition by a) as c
from tbl
) where c > 1) loop
update tbl set a=i where rowid = l_row.r;
i:=i+1;
end loop;
end;
/
我把它作为PL / SQL保存,以显示你所尝试的内容有什么问题,但@haki是完全正确的,你应该(并且可以)do this in plain SQL if at all possible。即使你需要它是PL / SQL,因为你在循环中做其他工作(如new_mail
字段可能建议的那样),那么你可能仍然可以在过程中进行单个更新,而不是一个围绕循环每次迭代更新。