2014-01-30 93 views
0

在Oracle 11g中,我使用的過程以下..有人可以提供更好的解決方案,以達到同樣的效果。For循環更新更好的選擇

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

請顯示完整的觸發代碼。 – OldProgrammer

+0

第一條建議:避免使用觸發器。改爲以編程方式執行任務。 – Arnab

回答

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 

重複運行有一些變化,特別是更新和合並通常非常接近,但有時交換在我的環境中更快;但兩者總是比循環更新快得多。您可以在自己的環境中重複這一點,並使用自己的數據傳播和數據量,如果性能是至關重要的,則應該這樣做;但是單個更新將比循環更快。不管你使用更新還是合併都不會有太大的區別。

+0

亞歷克斯,我已經提高了你..只是一個說明,你的LOOP查詢中的「DISTINCT」肯定會消耗COST,這會增加總時間。 –

+0

@OracleUser - 是的,但有重複的值,所以如果沒有,你最終會多次更新相同的行。我已經在做一些批次了;我從這個問題推斷出,但可能是錯誤的或有不同的比例。使用一個版本來拉取所有匹配的rowid並單獨更新每一行,它需要的時間大約是「distinct」版本的兩倍,因此更糟糕。 –