2010-01-08 122 views
3

假設所有外鍵都有適當的約束,是否有一個簡單的SQL語句來刪除在數據庫中任何地方沒有引用的行?SQL刪除孤兒

delete from the_table那樣簡單,只需跳過任何帶有子記錄的行?

我試圖避免手動循環表或添加類似where the_SK not in (a,b,c,d)

回答

6

您可能能夠使用擴展DELETE聲明在10g中包含錯誤日誌。

首先使用DBMS_ERRLOG創建一個記錄表(這僅僅是一個帶有一些額外的前綴列原始表的副本:ORA_ERR_MESG$, ..., ORA_ERR_TAG$)現在

execute dbms_errlog.create_error_log('parent', 'parent_errlog'); 

,你可以使用delete語句的錯誤日誌條款捕捉那些現有的完整性約束的所有行:

delete from parent 
    log errors into parent_errlog ('holding-breath') 
    reject limit unlimited; 

在這種情況下,「持有呼氣」的評論將進入ORA_ERR_TAG$列。

您可以閱讀完整的文檔here

如果父表非常龐大,而您只希望刪除一些流浪行,那麼您最終將得到一個parent_errlog表,它基本上與您的parent表重複。如果這也不行,你必須做的很長的路要走:

  1. 直接通過引用表(以下Tony's solution)的子表,或者,
  2. 環路在PL/SQL和捕捉任何例外(以下Confusion'sBob's solutions)。
+0

不錯,我不知道這個錯誤記錄。這樣我既不需要指定孩子也不需要遍歷行,但我認爲你應該添加「拒絕限制無限」(或任何數字)。事實上,刪除仍然失敗。 – RichN 2010-01-12 07:56:19

+0

斑點 - 添加到我的答案 – 2010-01-12 13:53:45

1

號很明顯,你可以這樣做(但我知道你寧願不):

delete parent 
where not exists (select null from child1 where child1.parent_id = parent.parent_id) 
and not exists (select null from child2 where child2.parent_id = parent.parent_id) 
... 
and not exists (select null from childn where childn.parent_id = parent.parent_id); 
2

最簡單的方法可能是編寫一個應用程序或存儲過程,該應用程序或存儲過程試圖一次一個地刪除表中的行,並簡單地忽略由於外鍵約束而導致的故障。之後,所有不在外鍵約束下的行都應該被刪除。根據所需/可能的性能,這可能是一個選項。要做到這一點

1

一種方式是寫像下面這樣:

eForeign_key_violation EXCEPTION; 
PRAGMA EXCEPTION_INIT(eForeign_key_violation, -2292); 

FOR aRow IN (SELECT primary_key_field FROM A_TABLE) LOOP 
    BEGIN 
    DELETE FROM A_TABLE A 
    WHERE A.PRIMARY_KEY_FIELD = aRow.PRIMARY_KEY_FIELD; 
    EXCEPTION 
    WHEN eForeign_key_violation THEN 
     NULL; -- ignore the error 
    END; 
END LOOP; 

如果一個孩子行存在的DELETE將失敗,沒有行會被刪除,你可以繼續你的下一個重點。

請注意,如果您的表格很大,則可能需要很長時間。