如何在没有日志的SQL中删除表的大数据?

use*_*343 104 sql-server sql-optimization sql-server-2008

我有一个大数据表.该表中有1000万条记录.

这个查询的最佳方法是什么?

   Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
Run Code Online (Sandbox Code Playgroud)

M.A*_*Ali 178

  1. 如果要删除该表中的所有行,最简单的选项是截断表,类似于

    TRUNCATE TABLE LargeTable
    GO
    
    Run Code Online (Sandbox Code Playgroud)

    截断表将简单地清空表,不能使用WHERE子句来限制被删除的行,也不会触发任何触发器.

  2. 另一方面,如果你要删除超过80-90%的数据,比如说你总共有11百万行,而你想要删除1000万另一种方法就是插入这100万行(你要保留的记录) )到另一个临时表.截断此大表并插回这些1百万行.

  3. 或者,如果权限/视图或其他具有此大表作为其基础表的对象不会因删除此表而受到影响,则可以将这些相对少量的行放入另一个表中,删除此表并创建具有相同模式的另一个表并导入这些行回到这个ex-Large表.

  4. 我能想到的最后一个选项是更改数据库Recovery Mode to SIMPLE,然后使用类似这样的while循环删除较小批量的行.

    DECLARE @Deleted_Rows INT;
    SET @Deleted_Rows = 1;
    
    
    WHILE (@Deleted_Rows > 0)
      BEGIN
       -- Delete some small number of rows at a time
         DELETE TOP (10000)  LargeTable 
         WHERE readTime < dateadd(MONTH,-7,GETDATE())
    
      SET @Deleted_Rows = @@ROWCOUNT;
    END
    
    Run Code Online (Sandbox Code Playgroud)

并且不要忘记将恢复模式更改回完全,我认为你必须备份以使其完全情感化(更改或恢复模式).

  • 还要记住,如果你截断一个表,你就不能让任何FK与它相关联. (12认同)
  • @Archont"未知案例的最佳解决方案",这不是梦想吗?不幸的是,你不能用任何一种药丸治愈每一种疾病; 我为不同的场景提出了一些可能的解决方案.不幸的是,这里没有子弹. (4认同)
  • 如果选择选项4,需要注意一点:根据表的使用方式,一次删除少于5000行可能是更好的选择,以避免[锁定升级](https://technet.microsoft.com/ EN-US /库/ ms184286(v = SQL.105)的.aspx). (4认同)

Fra*_*ein 75

@ m-ali的答案是正确的,但请记住,如果您不在每个块之后提交事务并执行检查点,那么日志可能会增长很多.我就是这样做的,并将这篇文章http://sqlperformance.com/2013/03/io-subsystem/chunk-deletes作为参考,包括性能测试和图表:

DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;


WHILE (@Deleted_Rows > 0)
  BEGIN

   BEGIN TRANSACTION

   -- Delete some small number of rows at a time
     DELETE TOP (10000)  LargeTable 
     WHERE readTime < dateadd(MONTH,-7,GETDATE())

     SET @Deleted_Rows = @@ROWCOUNT;

   COMMIT TRANSACTION
   CHECKPOINT -- for simple recovery model
END
Run Code Online (Sandbox Code Playgroud)

  • 如果可用磁盘空间有限,这应该是可接受的答案。如果没有“COMMIT TRANSACTION”和“CHECKPOINT”,日志仍在增长。感谢您说清楚这一点。 (3认同)
  • @FranciscoGoldenstein 那么,当您在“WHILE”循环本身中重复计算日期时,每次迭代中查询中使用的日期都会有所不同:“dateadd(MONTH,-7,GETDATE())”。 (2认同)

Bun*_*ter 44

您还可以使用GO +执行相同查询的次数.

DELETE TOP (10000)  [TARGETDATABASE].[SCHEMA].[TARGETTABLE] 
WHERE readTime < dateadd(MONTH,-1,GETDATE());
-- how many times you want the query to repeat
GO 100
Run Code Online (Sandbox Code Playgroud)

  • @ScottC,这不是循环,它只是重复查询(类似批处理),如果用完了行,它将无法删除任何内容。但这不会停止。如果删除的行用完了,您将得到类似(受影响的0行)的信息。 (2认同)
  • 嗯,似乎我可以执行它,并且它确实可以运行多次,但是在MS SQL Mgt Studio中,它显示了带有上述错误的红色花线(但是F5运行才起作用) (2认同)

小智 9

M.Ali的这种变化对我来说很好.它会删除一些,清除日志并重复.我正在看日志的增长,下降和重新开始.

DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
  BEGIN
   -- Delete some small number of rows at a time
    delete top (100000) from InstallLog where DateTime between '2014-12-01' and '2015-02-01'
    SET @Deleted_Rows = @@ROWCOUNT;
    dbcc shrinkfile (MobiControlDB_log,0,truncateonly);
END
Run Code Online (Sandbox Code Playgroud)


小智 9

@Francisco Goldenstein,只是一个小小的修正.设置变量后必须使用COMMIT,否则WHILE将只执行一次:

DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;

WHILE (@Deleted_Rows > 0)
BEGIN
    BEGIN TRANSACTION

    -- Delete some small number of rows at a time
    DELETE TOP (10000)  LargeTable 
    WHERE readTime < dateadd(MONTH,-7,GETDATE())

    SET @Deleted_Rows = @@ROWCOUNT;

    COMMIT TRANSACTION
    CHECKPOINT -- for simple recovery model

END
Run Code Online (Sandbox Code Playgroud)


Mic*_*een 7

如果您愿意(并且有能力)实现分区,那么这是一种有效的技术,可以在几乎没有运行时开销的情况下删除大量数据.但是,一次性运动不符合成本效益.