2010-08-10 267 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