2010-08-10 167 views
5

我有一個SQL Server 2005數據庫,並且我嘗試在相應的字段上放置索引以加速包含數百萬行的表的記錄的DELETEbig_table只有3列) ,但是現在的執行時間是DELETE,甚至是更長的! (例如1小時對比13分鐘)SQL Server DELETE與索引速度較慢

我與表格之間有關係,並且我篩選我的DELETE by的列在另一個表中。例如

DELETE FROM big_table 
WHERE big_table.id_product IN (
SELECT small_table.id_product FROM small_table 
WHERE small_table.id_category = 1) 

順便說一句,我也試着:

DELETE FROM big_table 
WHERE EXISTS 
(SELECT 1 FROM small_table 
WHERE small_table.id_product = big_table.id_product 
AND small_table.id_category = 1) 

,雖然它似乎略快於第一次運行,它仍然是慢了很多,有比沒有索引。

我創建了這些領域的指標:

  1. big_table.id_product
  2. small_table.id_product
  3. small_table.id_category

我.LDF文件DELETE期間增長了很多。

爲什麼我的表DELETE查詢速度比較慢?我以爲他們應該跑得更快。

UPDATE

好了,共識似乎是監守索引必須更新索引會減慢巨大DELETE。儘管如此,我仍然不明白爲什麼它不能同時排列所有行,只是在最後更新一次索引。

我的印象是,我的一些閱讀中指出,通過更快地搜索WHERE子句中的字段,索引將加速DELETE

Odetocode.com says:

「在他們的SELECT語句做DELETE和UPDATE命令的記錄搜索索引時只是正常工作。」

但後來在文章中,它說太多索引可能會損害性能。

答案鮑勃問題:在表

  1. 5500萬行
  2. 被刪除42000000行
  3. 類似SELECT聲明不會跑型(異常「系統。OutOfMemoryException異常」被拋出)

我嘗試了以下2個查詢:

SELECT * FROM big_table 
WHERE big_table.id_product IN (
SELECT small_table.id_product FROM small_table 
WHERE small_table.id_category = 1) 

SELECT * FROM big_table 
INNER JOIN small_table 
ON small_table.id_product = big_table.id_product 
WHERE small_table.id_category = 1 

兩個從SQL Server 2005中此錯誤消息25分鐘運行失敗後

An error occurred while executing batch. Error message is: Exception of type 'System.OutOfMemoryException' was thrown. 

數據庫服務器是具有7.5 GB RAM的較早的雙核Xeon機器。這是我的玩具測試數據庫:)所以它沒有運行其他任何東西。

我是否需要對我的索引做一些特殊處理後,我CREATE他們使他們正常工作?

+3

多少行是在表中?有多少行被刪除?一個類似的SELECT語句需要多長時間才能完成?瞭解SELECT語句的速度可能會提供一些關於索引如何影響DELETE的信息。 – bobs 2010-08-10 22:10:53

+0

55 mil mil rows,42 deleted,not complete,see above for more details – JohnB 2010-08-12 01:31:13

+0

這需要更長的時間,因爲當您執行刪除時,引用您的表的索引也必須更新。 – WOPR 2010-08-10 22:17:05

回答

27

索引使查找速度更快 - 就像書後面的索引。

更改數據(如DELETE)的操作較慢,因爲它們涉及操縱索引。考慮本書後面的相同索引。如果您添加,刪除或更改頁面,則還有更多工作要做,因爲您還必須更新索引。

0

您也可以嘗試TSQL擴展DELETE語法和檢查它是否可以提高性能:

DELETE FROM big_table 
FROM big_table AS b 
INNER JOIN small_table AS s ON (s.id_product = b.id_product) 
WHERE s.id_category =1 
+0

這完全沒有幫助;它需要完全相同的時間從'刪除big_table存在(從small_table選擇1,其中small_table.id_product = big_table.id_product和small_table.id_category = 1)' – JohnB 2010-08-12 13:41:50

1

我同意上述鮑勃評論 - 如果要刪除從大型表刪除索引可以採取大量數據除了刪除數據之外,還有一段時間,儘管它做生意的成本。由於它會刪除所有數據,導致發生重新索引事件。

關於日誌文件的增長;如果你沒有對你的日誌文件做任何事情,你可以切換到Simple日誌;但我希望您在更改之前瞭解可能對IT部門產生的影響。

如果您需要實時刪除,通常很好的解決方法是直接在表或其他表中標記數據爲非活動狀態,並將數據從查詢中排除;然後再回來並在用戶不注意沙漏時刪除數據。還有第二個理由來說明這一點。如果你從表中刪除大量數據(這是我根據你的日誌文件問題所假設的),那麼你可能會想要做一個indexdefrag來重新構建索引;如果您不喜歡手機上的用戶,那麼在數小時內完成這項工作就是一種好方法!

0

JohnB正在刪除約75%的數據。我認爲以下將是一個可能的解決方案,可能是更快的解決方案之一。而不是刪除數據,創建一個新表並插入您需要保留的數據。插入數據後在該新表上創建索引。現在放下舊桌子,並將新桌子重新命名爲與舊桌子相同的名稱。

上面當然假設有足夠的磁盤空間可用於臨時存儲重複的數據。

0

嘗試這樣的事情,以避免批量刪除(從而避免日誌文件的增長)

declare @continue bit = 1 

-- delete all ids not between starting and ending ids 
while @continue = 1 
begin 

    set @continue = 0 

    delete top (10000) u 
    from <tablename> u WITH (READPAST) 
    where <condition> 

    if @@ROWCOUNT > 0 
     set @continue = 1 

end