提高删除查询的性能

时间:2018-11-17 09:59:07

标签: sql sql-server tsql sqlperformance

我想从表B中删除条目,因此每个A_id(ID最高的条目)只有一个条目

表A

+----+------------+
| id |    name    |
+----+------------+
|  1 | Some name  |
|  2 | Other name |
+----+------------+

表B

+----+-------+------+
| id | stuff | A_id |
+----+-------+------+
|  1 | aab   |    1 |
|  2 | aac   |    1 |
|  3 | aad   |    2 |
|  4 | aae   |    1 |
|  5 | aak   |    1 |
|  6 | aal   |    2 |
+----+-------+------+

我当前的查询(效果很好):

DELETE FROM B 
WHERE id NOT IN (SELECT MAX(id)
                 FROM B
                 GROUP BY A_id)

将得到正确的结果:

+----+-------+------+
| id | stuff | A_id |
+----+-------+------+
|  5 | aak   |    1 |
|  6 | aal   |    2 |
+----+-------+------+

但是当表B中有很多行时,它非常慢。是否有任何方法可以提高查询的性能(或者可能以完全不同的方式来做?)

2 个答案:

答案 0 :(得分:3)

您正在删除大量行。那就是问题所在。删除操作的开销很大。

如果要删除表中的大量行(并且可能只占百分之几),则重新创建表通常会更快:

select b.*
into temp_b  -- actually, I wouldn't use a temporary table in case the server goes down
from b
where b.id = (select max(a.id) from b b2 where b2.id = b.a_id);

truncate table b;

insert into b
    select *
    from temp_b;

尝试此操作之前,请确保已备份b或至少将其副本保存在某个地方。

请注意,我更改了NOT IN的结构。我强烈不鼓励使用NOT IN,因为当子查询返回NULL值时语义并不直观。如果只有一个NULL值,那么WHERE将永远不会评估为TRUE。即使在这种情况下NULL值都不是问题,我还是强烈建议您使用其他替代方法,这样在NULL可能的情况下您就不会有问题。

为了提高SELECT的性能,您需要在b(a_id, id)上建立索引。您可能会发现这样的索引有助于您的原始查询。

答案 1 :(得分:2)

您的查询对我来说很好。

您的问题似乎是您拥有大量数据,并需要优化性能的方法。

您可以做的是具体化您的子查询,并确保将max_id编入索引,例如通过使其成为主键。

因此,创建一个临时表Max_B,并将子查询的结果存储在此表中。然后执行删除操作,然后删除临时表。