首页 文章

Oracle:更新非唯一字段

提问于
浏览
0

我需要更新一个非唯一字段 . 我有一个表tbl:

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

有什么不对?为什么循环不起作用?

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的第一条规则是 what ever you can do with SQL always do with SQL . 直写 for loop 导致在sql和pl / sql引擎之间进行上下文切换 . 即使oracle自动将其转换为批量语句(10g <),纯SQL仍然会更快 .

  • 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 值进行更新 . 我'm not sure why you'重新使用动态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 字段可能建议的那样),那么你可能仍然可以在过程中进行单个更新,而不是每次迭代一次更新循环 .

相关问题