2010-10-09 45 views
1

我試圖儘可能快地將記錄從一個表複製到另一個表。Oracle BULK FETCH和FORALL插入時的性能問題

目前,我有一個簡單的遊標循環類同此:

FOR rec IN source_cursor LOOP 
    INSERT INTO destination (a, b) VALUES (rec.a, rec.b) 
END LOOP; 

我想加快速度是超級快,所以我想一些批量操作(本體FETCH,那麼FORALL插入):

這是我對批量選擇/插入插入。

DECLARE 
    TYPE t__event_rows IS TABLE OF _event%ROWTYPE; 
    v__event_rows t__event_rows; 

    CURSOR c__events IS 
    SELECT * FROM _EVENT ORDER BY MESSAGE_ID; 
BEGIN 
    OPEN c__events; 
    LOOP 
    FETCH c__events BULK COLLECT INTO v__event_rows LIMIT 10000; -- limit to 10k to avoid out of memory 

    EXIT WHEN c__events%NOTFOUND; 

    FORALL i IN 1..v__event_rows.COUNT SAVE EXCEPTIONS 
     INSERT INTO destinatoin 
     (col1, col2, a_sequence) 
     VALUES 
     ( v__event_rows(i).col1, v__event_rows(i).col2, SOMESEQEUENCE.NEXTVAL); 


    END LOOP; 
    CLOSE c__events; 


END; 

我的問題是我目前在性能方面沒有看到任何大的進展。從我讀的它應該快10倍到100倍。

我在這裏錯過了一個瓶頸嗎?

+1

對於100,000行,即使使用批量插入,也需要大約300(!)秒 – Will 2010-10-09 00:17:27

+4

是否有原因在SELECT中有ORDER BY?對100,000行進行排序可能相當昂貴,而且似乎沒有必要。另外,您的%NOTFOUND檢查需要在FORALL之後發生 - 否則,如果您在上次迭代中讀取的行少於10,000行,則不會插入這些行。 – 2010-10-09 00:29:19

+0

謝謝!我將刪除順序,但我不認爲它會讓我相當的速度即時通訊尋找...我會嘗試進行tkprof分析並稍後更新。很好的捕獲%NOTFOUND – Will 2010-10-09 03:12:13

回答

7

唯一的好處你的代碼有超過一個簡單的INSERT + SELECT是你救的例外,再加上(賈斯汀指出的),你有一個毫無意義的ORDER BY這使得它做了一大堆毫無意義的工作。無論如何,您沒有任何代碼可以對保存的異常做任何事情。

我只是將它實現爲INSERT + SELECT

+2

而你可以在INSERT上使用LOG ERRORS子句來保存異常。 – 2010-10-11 15:59:21

0

你不必不必要地使用循環,直到它在編碼本身是必需的。

+0

這應該是一個評論,而不是一個答案。 – 2016-11-24 10:14:28

+0

是的,但代碼需要一些提示更好的性能問題。 – 2016-11-25 04:11:46

+0

是的,這就是爲什麼它應該是一個評論:) – 2016-11-25 05:48:25