对于循环更新更好的选择

时间:2014-01-30 18:26:28

标签: sql oracle loops join for-loop

在Oracle 11g中,我在 程序 中使用以下内容。有人可以提供更好的解决方案来实现相同的结果。

FOR REC IN 
(SELECT E.EMP FROM EMPLOYEE E 
JOIN 
COMPANY C ON E.EMP=C.EMP
WHERE C.FLAG='Y')
 LOOP
 UPDATE EMPLOYEE SET FLAG='Y' WHERE EMP=REC.EMP;
 END LOOP;

有更高效/更好的方法吗?我觉得这个方法会为每个找到的记录运行一个更新语句(如果我错了,请纠正我)。

以下是完整的实际代码:

create or replace 
PROCEDURE ACTION_MSC AS 
BEGIN
  -- ALL MIGRATED CONTACTS, CANDIDATES, COMPANIES, JOBS
  -- ALL MIGRATED CANDIDATES, CONTACTS
  FOR REC IN (SELECT DISTINCT AC.PEOPLE_HEX 
    FROM ACTION AC JOIN PEOPLE P ON AC.PEOPLE_HEX=P.PEOPLE_HEX 
    WHERE P.TO_MIGRATE='Y')
  LOOP
    UPDATE ACTION SET TO_MIGRATE='Y' WHERE PEOPLE_HEX=REC.PEOPLE_HEX;
  END LOOP;

  -- ALL MIGRATED COMPANIES
  FOR REC IN (SELECT DISTINCT AC.COMPANY_HEX 
    FROM ACTION AC JOIN COMPANY CM ON AC.COMPANY_HEX=CM.COMPANY_HEX
    WHERE CM.TO_MIGRATE='Y')
  LOOP
    UPDATE ACTION SET TO_MIGRATE='Y' WHERE COMPANY_HEX=REC.COMPANY_HEX;
  END LOOP;

  -- ALL MIGRATED JOBS
  FOR REC IN (SELECT DISTINCT AC.JOB_HEX 
    FROM ACTION AC JOIN "JOB" J ON AC.JOB_HEX=J.JOB_HEX
    WHERE J.TO_MIGRATE='Y')
  LOOP
    UPDATE ACTION SET TO_MIGRATE='Y' WHERE JOB_HEX=REC.JOB_HEX;
  END LOOP;

  COMMIT;
END ACTION_MSC;

1 个答案:

答案 0 :(得分:4)

你是对的,它会为找到的每条记录做一次更新。看起来你可以这么做:

UPDATE EMPLOYEE SET FLAG = 'Y'
WHERE EMP IN (SELECT EMP FROM COMPANY WHERE FLAG = 'Y')
AND FLAG != 'Y';

单个更新通常比循环中的多个单独行更新更快,更有效;有关其他示例,请参阅this answer。除了其他任何东西,你减少了PL / SQL和SQL之间的上下文切换次数,如果你有很多行,它们就会相加。当然,您可以随时使用自己的数据对此进行基准测试。

我已经添加了对当前标志状态的检查,因此您不会进行没有倒角的无意义更新。


比较方法可以相当容易地看到单个更新比循环中的更新更快;有一些人为的数据:

create table people (id number, people_hex varchar2(16), to_migrate varchar2(1));
insert into people (id, people_hex, to_migrate)
select level, to_char(level - 1, 'xx'), 'Y'
from dual
connect by level <= 100;

create table action (id number, people_hex varchar2(16), to_migrate varchar2(1));
insert into action (id, people_hex, to_migrate)
select level, to_char(mod(level, 200), 'xx'), 'N'
from dual
connect by level <= 500000;

所有这些都将更新action表中的一半行。循环更新:

begin
  for rec in (select distinct ac.people_hex 
    from action ac join people p on ac.people_hex=p.people_hex 
    where p.to_migrate='Y')
  loop
    update action set to_migrate='Y' where people_hex=rec.people_hex;
  end loop;
end;
/

Elapsed: 00:00:10.87

单次更新(回滚后;我将其留在块中以模仿您的程序):

begin
  update action set to_migrate = 'Y'
  where people_hex in (select people_hex from people where to_migrate = 'Y');
end;
/

Elapsed: 00:00:07.14

合并(回滚后):

begin
  merge into action a
  using (select people_hex, to_migrate from people where to_migrate = 'Y') p
  on (a.people_hex = p.people_hex)
  when matched then update set a.to_migrate = p.to_migrate;
end;
/

Elapsed: 00:00:07.00

重复运行有一些变化,特别是更新和合并通常非常接近但有时交换在我的环境中更快;但两者总是明显快于循环更新。您可以在自己的环境中使用自己的数据传播和卷重复此操作,如果性能至关重要,您应该这样做;但是单个更新将比循环更快。无论您使用更新还是合并,都不太可能产生太大的影响。