2013-11-05 21 views
0

我必須通過加入一組大表選擇一組值,然後從選定的值更新另一個大表。我目前正在按照下面的方法。但是我看到性能上的缺陷。上述類型的工作可以採用哪些替代方法?優化的方式來選擇和更新大表

DB服務器:甲骨文EE

DECLARE 
    CURSOR c1 
    IS 
    SELECT update_data FOR UPDATE OF abc; 
BEGIN 
    FOR update_data IN c1 
    LOOP 
    UPDATE klm 
    SET klm.xyz = update_data.cdf 
    WHERE update_data.abc = klm.abc; 
    END LOOP; 
    COMMIT; 
END; 

回答

1

最有可能的簡單更新將執行好得多。

你可以嘗試使用:

update klm t1 
set xyz = (select cdf from update_data t2 where t2.abc = t1.abc) 
where exists (select 1 from update_data t2 where t2.abc = t2.abc); 

commit; 

或者如果可能的話(對update_data.abc PK或唯一索引)

update (select t1.xyz, t2.cdf from klm t1, update_data t2 where t1.abc = t2.abc) 
) set xyz = cdf; 

commit; 
+0

感謝您的回答。我嘗試了第二種方法,但得到了一個錯誤SQL錯誤:ORA-01779:無法修改映射到非鍵保存表的列 – era

+0

而且我也無法理解第一個解決方案的哪裏存在部分。爲什麼你檢查t2.abc = t2.abc?謝謝 – era

+0

關於第一個答案; update_data又是一個生成的數據。我不認爲這是執行update_data SELECT查詢兩次(在SET部分和WHERE EXIST中)的最佳選擇。謝謝 – era

0

如果你有性能問題遍歷每個記錄,但表對於單個更新而言太大,您可以考慮使用BULK INTO ... LIMIT和FORALL批量更新。

CREATE TABLE klm (abc INTEGER, xyz INTEGER); 
CREATE TABLE update_data (abc INTEGER, cdf INTEGER); 

-- Have pairs of numbers (1000 rows) 
INSERT INTO klm SELECT rownum, rownum FROM dual CONNECT BY level <= 1000; 
-- Update every second row with 9999 
INSERT INTO update_data SELECT rownum * 2, 9999 FROM dual CONNECT BY level <= 500; 

DECLARE 
    CURSOR c1 
    IS 
    -- Select the key to be updated and the new value 
    SELECT abc, cdf FROM update_data; 
    -- Table type and table variable to store rows fetched from the cursor 
    TYPE t_update IS TABLE OF c1%rowtype; 
    update_tab t_update; 
BEGIN 
    OPEN c1; 
    LOOP 
    -- Fetch next 30 rows into update table 
    FETCH c1 BULK COLLECT INTO update_tab LIMIT 30; 
    -- Exit when there were no more rows fetched 
    EXIT WHEN update_tab.count = 0; 
    -- This is the key point; uses update_tab to bulk-bind UPDATE statement 
    -- and run it for 30 rows in a single context switch 
    FORALL i IN 1..update_tab.count 
     UPDATE klm 
     SET klm.xyz = update_tab(i).cdf 
     WHERE update_tab(i).abc = klm.abc; 
    COMMIT; 
    END LOOP; 
    CLOSE c1; 
END; 
/

這背後的基本原理是Oracle實際上有單獨的引擎運行SQL語句和PL/SQL程序。無論程序何時遇到SQL語句,都會將其交給SQL引擎執行。這被稱爲「上下文切換」,並且花費大量時間,尤其是在循環中完成時。

批量綁定旨在通過每[批量大小]記錄僅執行一次上下文切換來減少此開銷。同樣,這當然不像單個DML操作那樣有效,但對於大型表或複雜查詢,它可能是最好的可行解決方案。

我已經使用上面的方法來更新100M-500M記錄批量大小爲10K-100K的表,它工作正常。但是您需要在您的環境中嘗試批量大小以獲得最佳性能。

+0

Thx Kombajn。我可以理解LOOP。但不清楚選擇部分。你能解釋一下選擇邏輯嗎? – era

+0

SELECT語句只是從update_data表中選擇鍵(abc)和新值(cdf)(如果這就是你的意思)。無論如何,我補充了一些意見,我希望現在會更清楚。另外請看[Oracle批量綁定中的Oracle Base artice](http://www.oracle-base.com/articles/9i/bulk-binds-and-record-processing-9i.php) –