从具有非常少量扩展空间的表中删除行

Jac*_*rys 3 sql-server-2005 sql-server delete transaction-log

我有多个表备份了数据,现在暂时删除旧数据,由于空间限制,目前无法选择存档。我试过运行以下命令:

WHILE 1 = 1 
BEGIN 
    DELETE TOP ( 4000 )
    FROM    [OLD_TABLE] WITH (TABLOCKX)
    WHERE   [Date] < '2014-01-01 00:00:00'
    IF @@ROWCOUNT = 0 
        BREAK    
END
Run Code Online (Sandbox Code Playgroud)

这似乎有效,但非常缓慢……比如 17 多个小时,并且只删除了约 140,000 行。

这里的关键是这张表,其他需要减少的表非常大,大约有 50-6000 万行,大约有 16-17 GB 的空间。物理磁盘上只剩下 20 GB 的空间。我已经考虑将我想要保留的数据放在中间表中,截断和重新填充,但我担心在这些操作完成时我们会用完磁盘上的空间。这是一个生产数据库。我继承了这个烂摊子,我正在尝试清理它并试图清理这个数据库,这样我们就可以保持功能,直到我们可以安装新硬件。

是否有任何我(数据库管理的相对新手)没有遇到过的方法可以让我以更快、更有效的方式执行此操作?

*表名和列名已匿名。

编辑

查询执行计划

Pau*_*ite 8

就目前情况而言,查询:

DELETE TOP (4000)
FROM   [OLD_TABLE] WITH (TABLOCKX)
WHERE  [Date] < '2014-01-01 00:00:00'
Run Code Online (Sandbox Code Playgroud)

...必须扫描整个堆表,测试它找到的每一行,直到最终找到要删除的 4000 行。在下一次迭代中,整个业务又从第一个开始。假设每次扫描过程都以相同的(分配单元)顺序执行,随着时间的推移,这些扫描将花费越来越长的时间才能找到 4000 行。

[Date]列上创建索引将允许 SQL Server 更有效地查找 4000 行。它确实为每次删除增加了少量开销(因为新的非聚集索引也需要维护),但这与不每次执行扫描所节省的工作量相比,这算不了什么。索引也需要一定的空间,但不能太大。在恢复数据删除过程之前,您应该创建以下索引:

CREATE NONCLUSTERED INDEX <index_name>
ON dbo.OLD_TABLE ([Date]);
Run Code Online (Sandbox Code Playgroud)

顺便说一句,如果数据库启用了快照隔离或读提交快照隔离(即使没有主动使用!),TABLOCKX提示将不足以确保释放空堆页面。因此,您的堆表可能包含许多空页——这是一个问题,因为您的空间太少了。

解决此空间管理问题的标准方法是创建聚集索引(或发出ALTER TABLE REBUILD语句,但这需要 SQL Server 2008)。由于空间限制,创建聚集索引似乎也不适合您。考虑到空间问题,目前还没有明显的方法来解决这个问题。

在删除过程中要注意的一件事是事务日志使用的空间。如果数据库正在恢复FULL或正在BULK_LOGGED恢复,您将需要随时备份日志。如果数据库正在使用SIMPLE恢复,您可能需要CHECKPOINT不时发布手册以释放事务日志空间以供重用(否则物理文件可能会增长)。

如果您可以获得一些额外的临时存储,更好的方法可能是将要保留的数据批量导出到安全位置,删除表,重新创建它(最好使用聚集索引!)并重新加载保存的数据。这通常比增量删除过程快,但这取决于您的目标和优先级。