SQL复制删除数百万行的查询以提高性能
-
03-07-2019 - |
题
这是一次冒险。我从位于上一个问题中的循环重复查询开始,但每个循环将覆盖所有 1700万条记录,意味着需要数周(只需运行 * select count * from MyTable *
使用MSSQL 2005,我的服务器需要4:30分钟)。我从这个网站和这个帖子发现了信息。
已经到达下面的查询。问题是,对于任何类型的性能,这是在1700万条记录上运行的正确类型的查询吗?如果不是,那是什么?
SQL QUERY:
DELETE tl_acxiomimport.dbo.tblacxiomlistings
WHERE RecordID in
(SELECT RecordID
FROM tl_acxiomimport.dbo.tblacxiomlistings
EXCEPT
SELECT RecordID
FROM (
SELECT RecordID, Rank() over (Partition BY BusinessName, latitude, longitude, Phone ORDER BY webaddress DESC, caption1 DESC, caption2 DESC ) AS Rank
FROM tl_acxiomimport.dbo.tblacxiomlistings
) al WHERE Rank = 1)
解决方案
查看QueryPlan会有所帮助。
这可行吗?
SELECT m.*
into #temp
FROM tl_acxiomimport.dbo.tblacxiomlistings m
inner join (SELECT RecordID,
Rank() over (Partition BY BusinessName,
latitude,
longitude,
Phone
ORDER BY webaddress DESC,
caption1 DESC,
caption2 DESC ) AS Rank
FROM tl_acxiomimport.dbo.tblacxiomlistings
) al on (al.RecordID = m.RecordID and al.Rank = 1)
truncate table tl_acxiomimport.dbo.tblacxiomlistings
insert into tl_acxiomimport.dbo.tblacxiomlistings
select * from #temp
其他提示
您的数据库,服务器,存储或其某些组合可能会出现问题。 4:30选择计数*似乎非常高。
运行DBCC_SHOWCONTIG以查看表格的碎片程度,这可能会导致对大小的表格产生重大影响。
此外,要添加RyanKeeter的评论,请运行show plan,如果有任何表扫描,请在该表上为PK字段创建索引。
这样做不会更简单:
DELETE tl_acxiomimport.dbo.tblacxiomlistings
WHERE RecordID in
(SELECT RecordID
FROM (
SELECT RecordID,
Rank() over (Partition BY BusinessName,
latitude,
longitude,
Phone
ORDER BY webaddress DESC,
caption1 DESC,
caption2 DESC) AS Rank
FROM tl_acxiomimport.dbo.tblacxiomlistings
)
WHERE Rank > 1
)
在查询分析器中运行:
SET SHOWPLAN_TEXT ON
然后请求查询分析器运行您的查询。 SQL Server将生成查询计划并将其放入结果集中,而不是运行查询。
向我们展示查询计划。
1700万条记录什么都没有。如果只需要4:30进行选择计数(*),那么就会出现严重的问题,可能与服务器内存不足或者处理器老旧有关。
为了提高性能,请修理机器。将其泵送至2GB。 RAM现在非常便宜,其成本远远低于你的时间。
当查询进行时,处理器或磁盘是否会发生颠簸?如果没有,那么有些东西阻止了这些电话。在这种情况下,您可以考虑将数据库置于单用户模式中,以确定运行清理所需的时间。
所以你要删除所有未排名第一的记录?可能值得将连接与前1个子查询进行比较(这可能也适用于2000年,因为排名仅为2005及以上)
您是否需要在一次操作中删除所有重复项?我假设您正在执行某种内务管理任务,您可能可以分段执行。
基本上创建一个循环所有记录的游标(脏读)并删除每个记录的欺骗。总体来说它会慢得多,但每次操作都会相对最小。然后你的家务管理成为一个持续的后台任务而不是每晚一次。
首先选择临时表的建议是最好的选择。你也可以使用类似的东西:
set rowcount 1000
在运行删除之前。删除1000行后,它将停止运行。然后反复运行,直到删除0条记录。
如果我正确理解你的查询与
相同DELETE tl_acxiomimport.dbo.tblacxiomlistings
FROM
tl_acxiomimport.dbo.tblacxiomlistings allRecords
LEFT JOIN (
SELECT RecordID, Rank() over (Partition BY BusinessName, latitude, longitude, Phone ORDER BY webaddress DESC, caption1 DESC, caption2 DESC ) AS Rank
FROM tl_acxiomimport.dbo.tblacxiomlistings
WHERE Rank = 1) myExceptions
ON allRecords.RecordID = myExceptions.RecordID
WHERE
myExceptions.RecordID IS NULL
我认为应该跑得更快,我倾向于避免使用“IN”。在可能的情况下支持JOIN的条款。
您可以通过简单地在FROM部分调用 SELECT *
或 SELECT COUNT(*)
来安全地测试速度和结果,例如
SELECT *
FROM
tl_acxiomimport.dbo.tblacxiomlistings allRecords
LEFT JOIN (
SELECT RecordID, Rank() over (Partition BY BusinessName, latitude, longitude, Phone ORDER BY webaddress DESC, caption1 DESC, caption2 DESC ) AS Rank
FROM tl_acxiomimport.dbo.tblacxiomlistings
WHERE Rank = 1) myExceptions
ON allRecords.RecordID = myExceptions.RecordID
WHERE
myExceptions.RecordID IS NULL
这是我更喜欢JOIN方法的另一个原因 我希望有帮助
这看起来很好,但您可以考虑将数据选择到临时表中并在删除语句中使用它。我注意到这样做会带来巨大的性能提升,而不是在一个查询中完成所有操作。
请记住,在进行大型删除时,最好先备份一个好的备份。(我通常也会将删除的记录复制到另一个表中以防万一,我需要立即恢复它们。)
除了建议使用truncate之外,我还有幸使用此模板从表中删除大量行。我不记得了,但我认为使用该事务有助于保持日志文件的增长 - 可能是另一个原因 - 不确定。在执行以下操作之前,我通常会将事务日志记录方法切换为简单:
SET ROWCOUNT 5000 WHILE 1 = 1 BEGIN begin tran DELETE FROM ??? WHERE ??? IF @@rowcount = 0 BEGIN COMMIT BREAK END COMMIT END SET ROWCOUNT 0