我有一个大数据表。该表中有1000万条记录。
什么是此查询的最佳方法
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
我有一个大数据表。该表中有1000万条记录。
什么是此查询的最佳方法
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
Answers:
如果要删除该表中的所有行,最简单的选择是截断表,例如
TRUNCATE TABLE LargeTable
GO
截断表只会清空表,您不能使用WHERE子句来限制要删除的行,并且不会触发任何触发器。
另一方面,如果要删除80%到90%以上的数据,则说如果您总共有1,100万行并且要删除1000万行,则另一种方法是插入这100万行(要保留的记录)到另一个登台表。截断此大表,然后再插入这100万行。
或者,如果具有此大表作为其基础表的权限/视图或其他对象未因删除此表而受到影响,则可以将这些相对少量的行放入另一个表中,然后将该表删除并创建具有相同模式的另一个表并将其导入排回到这个前大表中。
我能想到的最后一个选择是更改数据库的内容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
并且不要忘记将恢复模式更改回完全状态,我认为您必须备份以使其完全有效(更改或恢复模式)。
optimal solution for unknown case
那是梦想,不是吗?不幸的是,您不能用任何一种药来治愈所有疾病。我已经针对不同的情况提出了一些可能的解决方案。不幸的是,这里没有银弹。
@ 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
COMMIT TRANSACTION
,CHECKPOINT
日志还在增长。感谢您澄清这一点。
@Deleted_Rows
与10000 进行比较,否则可能会因无限期删除少量数据集而导致无限循环。因此WHILE (@Deleted_Rows = 10000)
-一旦没有完整的“页面”数据删除,它就会停止。在您的实现中,WHILE (@Deleted_Rows > 0)
while循环即使只删除了一行也将再次执行,而下一次执行可能还会发现要删除的一行或两行-从而导致无限循环。
WHILE
循环本身中反复计算日期,因此每次迭代查询中使用的日期都会有所不同dateadd(MONTH,-7,GETDATE())
。
WHILE
循环的不同迭代之间删除的新记录。
您还可以使用GO +来执行同一查询多少次。
DELETE TOP (10000) [TARGETDATABASE].[SCHEMA].[TARGETTABLE]
WHERE readTime < dateadd(MONTH,-1,GETDATE());
-- how many times you want the query to repeat
GO 100
GO xx
应从哪个MS SQL Server运行?我收到“找不到存储过程”错误。如果没有该GO
命令,它将正常运行。
@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
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
# of rows
一次删除的内容以及该WHERE
子句。奇迹般有效!
您可以使用while循环删除小批量,如下所示:
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
WHILE @@ROWCOUNT > 0
BEGIN
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
END
另一个用途:
SET ROWCOUNT 1000 -- Buffer
DECLARE @DATE AS DATETIME = dateadd(MONTH,-7,GETDATE())
DELETE LargeTable WHERE readTime < @DATE
WHILE @@ROWCOUNT > 0
BEGIN
DELETE LargeTable WHERE readTime < @DATE
END
SET ROWCOUNT 0
可选的;
如果启用了事务日志,请禁用事务日志。
ALTER DATABASE dbname SET RECOVERY SIMPLE;
如果您使用的是SQL Server 2016或更高版本,并且您的表具有基于要删除的列的分区(例如Timestamp列),则可以使用此新命令按分区删除数据。
使用(分区({|} [,... n]))截断表
这将仅删除所选分区中的数据,并且应该是从表的一部分删除数据的最有效方法,因为它不会创建事务日志,并且处理速度与常规截断一样快,但是不会删除所有数据从桌子上。
缺点是,如果您的表未设置分区,那么您需要继续学习并使用常规方法删除数据,然后使用分区重新创建表,以便将来可以执行此操作,这就是我所做的。我将分区的创建和删除添加到了插入过程本身中。我的表有5亿行,因此这是减少删除时间的唯一选择。
有关更多详细信息,请参见以下链接:https : //docs.microsoft.com/zh-cn/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-2017
在重新创建带有所需数据分区的表之前,下面是我首先删除数据的操作。该查询将在指定的时间段内运行几天,直到删除数据。
:connect <<ServerName>>
use <<DatabaseName>>
SET NOCOUNT ON;
DECLARE @Deleted_Rows INT;
DECLARE @loopnum INT;
DECLARE @msg varchar(100);
DECLARE @FlagDate datetime;
SET @FlagDate = getdate() - 31;
SET @Deleted_Rows = 1;
SET @loopnum = 1;
/*while (getdate() < convert(datetime,'2018-11-08 14:00:00.000',120))
BEGIN
RAISERROR( 'WAIT for START' ,0,1) WITH NOWAIT
WAITFOR DELAY '00:10:00'
END*/
RAISERROR( 'STARTING PURGE' ,0,1) WITH NOWAIT
WHILE (1=1)
BEGIN
WHILE (@Deleted_Rows > 0 AND (datepart(hh, getdate() ) >= 12 AND datepart(hh, getdate() ) <= 20)) -- (getdate() < convert(datetime,'2018-11-08 19:00:00.000',120) )
BEGIN
-- Delete some small number of rows at a time
DELETE TOP (500000) dbo.<<table_name>>
WHERE timestamp_column < convert(datetime, @FlagDate,102)
SET @Deleted_Rows = @@ROWCOUNT;
WAITFOR DELAY '00:00:01'
select @msg = 'ROWCOUNT' + convert(varchar,@Deleted_Rows);
set @loopnum = @loopnum + 1
if @loopnum > 1000
begin
begin try
DBCC SHRINKFILE (N'<<databasename>>_log' , 0, TRUNCATEONLY)
RAISERROR( @msg ,0,1) WITH NOWAIT
end try
begin catch
RAISERROR( 'DBCC SHRINK' ,0,1) WITH NOWAIT
end catch
set @loopnum = 1
end
END
WAITFOR DELAY '00:10:00'
END
select getdate()
如果我说没有循环,我可以使用GOTO
语句使用SQL Server删除大量记录。exa。
IsRepeat:
DELETE TOP (10000)
FROM <TableName>
IF @@ROWCOUNT > 0
GOTO IsRepeat
像这样,您可以使用较小的删除大小删除大量数据。
让我知道是否需要更多信息。