2016-07-23 96 views
2

擁有包含數百萬條記錄的父表和具有指向父表的主鍵的三個子表的外鍵。像這樣:從SQL Server中的多個表中刪除多條記錄的最佳做法

Parent 
parent_id (PK) \  Child1 
       |  child1_id (PK) 
       |---- parent_id (FK) 
       | 
       |  Child2 
       |  child2_id (PK) 
       |---- parent_id (FK) 
       | 
       |  Child3 
       |  child3_id (PK) 
       |---- parent_id (FK) 

什麼是硬的最佳實踐從Parent刪除的記錄幾十萬?我想刪除以下條件:DELETE FROM PARENT WHERE [STATUS] = 'DONE'。有沒有辦法在刪除發生時鎖定表格?那麼其他記錄可以插入所有這些表中? 我可以想到的選項:

  1. 使用CASCADE DELETE上的外鍵。
  2. 使用軟刪除:開始一個事務,UPDATE parent SET [DELETED] = 1 WHERE [STATUS] = 'DONE',刪除每個孩子與父母的ID,然後硬刪除父母並提交。
  3. 類似於2,但使用過程並將這些ID保存在表變量中,以便我不需要在Parent表中添加新的[DELETED]列。
  4. 選擇要刪除的ID SELECT parent_id FROM parent WHERE [STATUS] = 'DONE'然後做一個批量刪除傳遞所有這些ID。 (這真的很糟糕,所以我放棄了它)。

我正在使用SQL Server 2014和spring jdbc。

+1

我建議在外鍵上使用級聯刪除,每次刪除多達50,000條記錄的批處理。刪除大量記錄通常會在將其分解爲小批量時加速。 –

+1

我是測量操作進度的粉絲,所以我會考慮編寫一個程序來刪除小部分的記錄。或者使用ON CASCADE DELETE或自己做。 –

回答

1

我更喜歡使用前X

所以對於每個子表批量刪除:對於每個子表

DELETE TOP 10000 
FROM child1 
FROM child 1 as c1 
INNER join parent 
On parent_Id = c1.parent_id 
AND parent.[STATUS] = 'DONE' 

重複多批次。

定期地,您可以刪除沒有孩子的父記錄。

DELETE TOP 10000 
FROM parent 
FROM parent as p 
Left outer join child1 c1 
On p.parent_Id = c1.parent_id 
AND c1.child_id IS NULL 
Left outer join child2 c2 
On p.parent_Id = c2.parent_id 
AND c2.child_id IS NULL 
Left outer join child3 c3 
On p.parent_Id = c3.parent_id 
AND c3.child_id IS NULL 
WHERE parent.[STATUS] = 'DONE' 

每個家長有多少個孩子將決定您運行父刪除的頻率。你當然可以改變X我會測試小,然後增加說50000

+0

因爲可以插入新的'DONE'記錄,所以我不能僅僅執行'刪除頂部10000 ...父母'[STATUS] ='DONE'',我不想刪除這些記錄。我可能會保存所有記錄的ID在表變量或臨時表上刪除,然後按照您的建議批量刪除。 – otonakav

+1

如何通過附加子句來聲明限制日期或通過id命令來確保只刪除較舊的記錄 – Mike

+0

聽起來很好,謝謝 – otonakav

1

有沒有辦法在刪除發生時鎖定表(s)?

是的。正如你所建議的那樣,一次批量操作而不是數百萬條記錄會改善併發訪問。

我從來沒有使用級聯刪除,因爲它是陰險的:它可以很好地處理行的操作,但可以防止數百萬人受到冷遇。我從不使用TOP,因爲它不合邏輯:它可以使用任意數字而不是數據的某個方面。

每次我寫這樣的程序時,我都使用了相同的技巧。從底部開始,循環會刪除主鍵上的數據子集。當刪除返回0行受影響,移動到下一個表,等等,直到你可以刪除最上面一行,沒有任何懸掛引用。基本缺失看起來是這樣的:

while @nrows > 0 begin 
    delete from Child3 
    where -- limitation criteria -- and 
    parent_id = (
     select min(parent_id) 
     from Parent 
     where Status = 'DONE' 
    ) 
    set @nrows = @@rowcount 
done 

如果你不能在同一時間刪除一個parent_id所有行,因爲性能原因,找一些限制子集,並循環上。也許是一個日期,並且一次刪除一個月或一年。如果您一次可以切實刪除多個家長,請一次選擇其中的一部分,並使用exists而不是最低限度。

幸運的是,爲此目的,您不需要用戶定義的事務。無論如何,這些行都是烤麪包,您可以隨時隨地重新啓動,無論是否存在「完成」父項。