2011-04-22 19 views
5

我想從表中刪除很多行。我想嘗試一下將我想要刪除的行放入遊標中,然後繼續在遊標的每一行進行讀取,刪除和提交,直到它爲空。如何從光標獲取,刪除,提交

在下面的代碼中,我們是fetching行並將它們放入TYPE

我該如何修改下面的代碼以從圖片中刪除TYPE,並且只需在光標本身上執行fetch,delete,commit即可。

OPEN bulk_delete_dup; 
    LOOP 
     FETCH bulk_delete_dup BULK COLLECT INTO arr_inc_del LIMIT c_rows; 

     FORALL i IN arr_inc_del.FIRST .. arr_inc_del.LAST 
       DELETE FROM UIV_RESPONSE_INCOME 
       WHERE ROWID = arr_inc_del(i); 

     COMMIT; 
     arr_inc_del.DELETE; 
     EXIT WHEN bulk_delete_dup%NOTFOUND; 
    END LOOP; 
    arr_inc_del.DELETE; 
    CLOSE bulk_delete_dup; 
+4

我將確保基於集合的解決方案不是試圖去光標的路線前一個選項。基於集合的解決方案將比光標效率更高,代碼更少,更易於理解......請問爲什麼標準刪除在您的方案中不起作用? – 2011-04-22 16:25:36

+0

在我工作的地方有一項政策。沒有單個事務應該運行超過8秒鐘。這就是爲什麼我不能簡單地在一行sql中做到這一點。 – Omnipresent 2011-04-22 17:48:40

+1

因此,這個可疑策略的解決方案是將所有事務拆分爲單行事務,並犧牲數據的一致性? – 2011-04-26 13:05:17

回答

14

爲什麼要批量提交?這隻會降低你的處理速度。除非有試圖修改您要刪除,這似乎是因爲其他原因有問題的行其他場次,最有效的方法是簡單地用一個DELETE刪除數據,即

DELETE FROM uiv_response_income uri 
WHERE EXISTS( 
    SELECT 1 
     FROM (<<bulk_delete_dup query>>) bdd 
    WHERE bdd.rowid = uri.rowid 
) 

中當然,根據光標背後的查詢是如何設計的,可能會有更好的書寫方式。

如果你真的想消除BULK COLLECT(這將大幅下降緩慢的過程),你可以使用語法的WHERE CURRENT做DELETE

SQL> create table foo 
    2 as 
    3 select level col1 
    4 from dual 
    5 connect by level < 10000; 

Table created. 

SQL> ed 
Wrote file afiedt.buf 

    1 declare 
    2 cursor c1 is select * from foo for update; 
    3 l_rowtype c1%rowtype; 
    4 begin 
    5 open c1; 
    6 loop 
    7  fetch c1 into l_rowtype; 
    8  exit when c1%notfound; 
    9  delete from foo where current of c1; 
10 end loop; 
11* end; 
SQL>/

PL/SQL procedure successfully completed. 

注意,但是,既然你必須鎖定該行(使用FOR UPDATE子句),則不能在循環中提交提交。做一個承諾將釋放你曾與FOR UPDATE請求的鎖,你會得到一個ORA-01002:取了順序錯誤的

SQL> ed 
Wrote file afiedt.buf 

    1 declare 
    2 cursor c1 is select * from foo for update; 
    3 l_rowtype c1%rowtype; 
    4 begin 
    5 open c1; 
    6 loop 
    7  fetch c1 into l_rowtype; 
    8  exit when c1%notfound; 
    9  delete from foo where current of c1; 
10  commit; 
11 end loop; 
12* end; 
SQL>/
declare 
* 
ERROR at line 1: 
ORA-01002: fetch out of sequence 
ORA-06512: at line 7 

如果去掉鎖定和避免你可能無法得到運行時錯誤WHERE CURRENT OF語法,根據您從光標中獲取的值刪除數據。但是,這仍然在跨越提交進行提取,這是一個糟糕的做法,從根本上增加了您至少間歇性地得到ORA-01555:快照太舊錯誤的機率。與單個SQL語句或BULK COLLECT選項相比,它的速度也會很慢。

SQL> ed 
Wrote file afiedt.buf 

    1 declare 
    2 cursor c1 is select * from foo; 
    3 l_rowtype c1%rowtype; 
    4 begin 
    5 open c1; 
    6 loop 
    7  fetch c1 into l_rowtype; 
    8  exit when c1%notfound; 
    9  delete from foo where col1 = l_rowtype.col1; 
10  commit; 
11 end loop; 
12* end; 
SQL>/

PL/SQL procedure successfully completed. 

當然,你也必須確保你的過程是萬一你處理行的一些子集,並有一些數量不明的臨時提交的過程中死亡之前重新啓動。如果DELETE足以導致該行不再從光標返回,那麼您的進程可能已經可以重新啓動。但總的來說,如果您嘗試將單個操作分解爲多個事務,則這是一個問題。

+0

我不能滿足你的要求。只是碰巧我正在使用當前的代碼來處理代碼,並一直在獲取序列錯誤。當我回到SO時,很高興找到解決方案,看看我是否有任何迴應。謝謝 – Omnipresent 2011-04-22 17:47:39

+0

我就是這麼做的,有兩條評論 - 1,如果可能的話,使用ROWID獲取和刪除,並且儘可能少地提交。如果某個其他的rouge事務持續保留了您要刪除的行超過了您等待的8秒鐘,您仍然可能會等待。 – 2011-04-24 01:08:16

1

有幾件事。從貴公司的「8秒內沒有交易」規則(8秒,你在德克薩斯州?)看來,你有一個生產數據庫實例,傳統上支持做OLTP的應用程序(插入1行,更新2行等),並且現在也成爲批處理db(刪除50%的行並替換爲1mm的新行)。

批處理應該與OLTP實例分開。在批處理(「數據工廠」)實例中,我不會嘗試在這種情況下刪除,我可能會做一個CTAS,刪除舊錶,重命名新表,重建索引/統計信息,重新編譯無效objs方法。

假設你在你的「8秒」實例中進行批處理,你可能會發現你的公司將來會要求越來越多,所以請DBA儘可能多的回滾,因爲你可以得到,並希望通過提交跨越提交(光標選擇驅動刪除,提交每1000行左右,使用rowid刪除),您不會獲得快照太舊。

如果DBA無法提供幫助,您可以先創建一個包含要刪除的rowid的臨時表,然後遍歷臨時表以從主表中刪除(避免提交提交),但貴公司可能會對此有一些規則,以及這是另一種(基本)批處理技術。

喜歡的東西:

declare 

-- assuming index on someCol 
cursor sel_cur is 
select rowid as row_id 
from someTable 
where someCol = 'BLAH'; 

v_ctr pls_integer := 0; 
begin 
for rec in sel_cur 
loop 
    v_ctr := v_ctr + 1; 
    -- watch out for snapshot too old... 
    delete from someTable 
    where rowid = rec.row_id; 
    if (mod(v_ctr, 1000) = 0) then 
    commit; 
    end if; 
end loop; 
commit; 

end;