0

我需要更新一个非唯一字段。我有一张桌子:

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

有什么问题?为什么循环不起作用?

4

2 回答 2

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 仍然会更快。

于 2013-04-09T05:52:51.453 回答
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 是非常正确的,如果可能的话,您应该(并且可以)用纯 SQL 执行此操作。即使您需要它是 PL/SQL,因为您正在循环中执行其他工作(如该new_mail领域可能建议的那样),那么您仍然可以在过程中进行一次更新,而不是围绕环形。

于 2013-04-08T18:57:09.233 回答