来自地狱的20小时查询

时间:2010-12-29 15:59:21

标签: sql tsql sql-server-2008

我在一个包含50万条记录的表上运行此查询,其中包含大约7个字段:

delete from qvalues where rid not in  
(
select min(rid) from qvalues
group by name,compound,rowid
having COUNT(*)>1)

and rid not in  (select min(rid) from qvalues

group by name,compound,rowid
having COUNT(*)=1);

为什么这么长时间?

我该怎么做才能优化它?

即时运行sql server 2008

6 个答案:

答案 0 :(得分:2)

最好的办法是查看执行计划,看看花费的时间最长。我首先将两个not in查询减少为一个:

delete 
from qvalues 
where rid not in
(
    select min(rid)
    from qvalues
    group by name, compound, rowid
    having count(1) >= 1
)

您可能还想考虑在namecompoundrowid

上添加索引

答案 1 :(得分:1)

不知道所涉及的实际数据,我可以给出一些一般性建议:分别运行每个子查询。

另外,我读错了,或者你是否删除了此表中的所有条目(如果删除是唯一的)?

答案 2 :(得分:1)

1 - 使用批处理。这可让您恢复,并让您了解进度。举个例子:

DECLARE @MSG Varchar(max)

WHILE 1=1
    BEGIN
        DELETE TOP (100000) qvalues
        FROM qvalues WITH (TABLOCKX)
        <logic here>
        IF @@ROWCOUNT < 100000 BREAK        
        SET @Msg = 'Deleted another 10 Million'
        SET @Msg = @Msg + ' ' +CONVERT(varchar(20),GETDATE(),101)+' '+CONVERT(varchar(20),GETDATE(),108) 
        RAISERROR(@Msg, 0, 1) WITH NOWAIT
    END

请注意,我还添加了一个WITH (TABLOCKX)提示,它会锁定表锁并消除行级锁定。它会导致并发读取问题,但希望您在删除时没有其他任何查询该表的内容。

2 - 修复你的逻辑如果没有更好地了解你的表结构,这是不可能的,但有些选项是: - 使用要比较的值实现表格并进行连接。如果删除足够大,则可以在连接字段的临时表上创建聚簇索引。我用了很多东西并取得了巨大的成功。 - 如果您希望删除大部分记录,SELECT INTO新表并删除旧表。这是一个记录最少的操作,并且在SQL Server 2008上运行得非常快,而删除则需要记录每行的值。 - 删除所有索引,但是您用于选择的内容和聚簇索引。如果聚类索引是查询的相关群集,则保留聚簇索引通常可以删除此类型。

答案 3 :(得分:1)

除了考虑批处理和索引之外,您还可以重写查询本身以删除子查询并提高效率。

;WITH cte As
(
SELECT ROW_NUMBER() OVER (PARTITION BY name, compound, rowid ORDER BY rid) AS RN
FROM qvalues
)
DELETE FROM cte WHERE RN > 1

每组可能会有多少重复?如果很多,可以更快地插入记录以保存到新表中,然后删除并重命名。

答案 4 :(得分:0)

首先想到:

delete from qvalues where rid not in  
(
select min(rid) from qvalues
group by name,compound,rowid
having COUNT(*)>1

UNION

select min(rid) from qvalues
group by name,compound,rowid
having COUNT(*)=1);

也许这也是一个好主意,确保sql-server知道你正在做一个“不相关的子选择”(因为“相关的子选择”需要更长的时间):

delete from qvalues a where a.rid not in  
(
select min(b.rid) from qvalues b
group by b.name,b.compound,b.rowid
having COUNT(*)>1

UNION

select min(c.rid) from qvalues c
group by c.name,c.compound,c.rowid
having COUNT(*)=1);

当然你应该考虑使用索引(尤其是关于rid,还要关于name,compound,rowid)

我的SQL没有经过测试 - 我希望你能够了解我想要展示的内容。

PS:你的sql需要大量的计算(特别是HAVING条款),你能否尝试为你的问题找到另一种解决方案?

答案 5 :(得分:0)

您的机器设置是什么,您是否有足够的内存,在查询运行时(CPU,内存,磁盘IO),您在哪里可以看到最大的利用率?