2015-04-07 25 views
1

我們已棄用某個功能並正在清除一些數據。在完全刪除需要太多即時應用程序開發的列之前,我們只需簡單清除其包含的數據(應用程序支持的數據)。可能拖延UPDATE x SET y = NULL語句

但是,一個簡單的UPDATE foo SET bar = NULL似乎異常昂貴。在數據庫的測試副本中,在我們取消它之前,它運行了三個多小時。

我們再次嘗試使用表鎖和READ UNCOMMITTED隔離級別進行查詢,但無濟於事(在另外三個小時後取消)。

該表包含大約112000行,每行的列包含大約41400字節(所以我們正在清除大於4GiB的數據)。雖然它有相當多的數據,但我們發現將剩餘列複製到新表中,刪除舊錶並重新命名新表會實際上更快。請注意,我們不知道要花多少時間才能完成UPDATE,否則我們在3小時後停止,但可能每天5,6,12。

該表在這些操作過程中的併發訪問精確度爲零。

有人會對我們的方案有任何建議嗎? copy + drop + rename是否是最好的選擇?如果是的話,還有什麼特別的建議使它儘可能安全?

對我們來說,一個可能天真的假設是DBMS將能夠在正常的UPDATE聲明的幕後制定一個複製/交換策略,如果給出足夠鬆散的提示。可能嗎?

+0

看起來像記錄事務的典型案例比您預期的要貴。嘗試使用循環來更新塊,例如1000,一次一排。 – HABO

+0

@HABO聽起來不錯,謝謝。看看[this](https://social.msdn.microsoft.com/Forums/sqlserver/en-US/90a44905-f3e3-4f82-9c83-e875a1bf1cb1/this-would-be-a-great-addition-to- tsql)似乎沒有一種「好」的方式去實現它(即不需要每個批次掃描表)。使用我們的謂詞('bar IS NOT NULL')進行掃描需要很多時間(已經7分鐘並且計數,這取決於批量的數量會非常快)。我的猜測是由於列的大小,數據不在行中內聯。你知道分裂這種交易的慣用方法嗎? – tne

+1

一個常見的方法就像'update ...',其中FooId(從Foo中選擇頂部1000 FooId,其中Bar不是NULL)'循環,直到'@@ RowCount'爲零。在你的情況下,在臨時表中收集適當的'FooId'並解決這個問題以避免重新掃描'Foo'是有意義的。在處理它們時刪除或標記臨時錶行。並且嘗試不同的塊大小沒有壞處,例如100行,直到找到(相對)最佳位置。 – HABO

回答

2

我們結束了複製和交換表。

評論中的question linked by Stephan包含一些有用的指導,說明如何在操作過程中保持數據集在線。特別是參見Mitch Schroeter的this answer,它基本上建立了在轉移發生時新舊桌子結合的觀點。

因爲我們不需要保持數據集在線,所以這是過度的(特別是考慮到其他數據集非常小)。相反:

CREATE TABLE _foobar (id INT IDENTITY PRIMARY KEY, foo INT, bar INT NULL); 
SET IDENTITY_INSERT _foobar ON; 
INSERT _foobar (id, foo, bar) SELECT id, foo, NULL FROM foobar; 
SET IDENTITY_INSERT _foobar OFF; 
DROP TABLE foobar; 
EXECUTE sp_rename '_foobar', 'foobar'; 

整個操作耗時14秒,這似乎很難擊敗我們的場景。

一些提示/評論:

  • 確保CREATE TABLE語句產生匹配(例如使用像VS或SSMS工具)的模式。
  • 不要忘記IDENTITY列。這意味着您需要爲INSERT語句顯式編寫列列表,當然還需要爲該表設置IDENTITY_INSERT。詳情請參閱MSDN documentation

結論:

  • 這似乎是根據this有分割在多個交易正常UPDATE交易在更高的層次來管理一致性,沒有簡單的方法。正如那裏和HABO所建議的那樣,所有解決方案似乎都需要掃描每個批次的請求謂詞,或者使用臨時表來一次存儲與謂詞匹配的行的鍵,並將其用於每個批次(其中應該總是更快,因爲PK始終是索引的)。
  • 似乎沒有簡單的方法來執行復制/交換,同時保持聯機操作。同樣,請參閱this以瞭解手動設置聯合視圖的方法。
  • 如果數據集的其餘部分非常小(快速完整地複製)並且不需要保持聯機狀態,則可以使用上面更直接的方法。免責聲明:如果您有DBA,請諮詢您的DBA,如果您不能100%確定自己在做什麼,這可能會很危險。