Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/sql-server-2008/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 如何在500000+;行表_Sql_Sql Server 2008_Tsql_Sql Delete_Truncate - Fatal编程技术网

Sql 如何在500000+;行表

Sql 如何在500000+;行表,sql,sql-server-2008,tsql,sql-delete,truncate,Sql,Sql Server 2008,Tsql,Sql Delete,Truncate,假设我们有30列500000行的表Sales。我想删除表中的400000个(其中“toDelete='1') 但我有几个限制: 该表是“经常”读/写的,我不希望长时间的“删除”会花费很长时间并锁定该表太长时间 我需要跳过事务日志(如使用截断),但在执行“删除…其中…”(我需要设置一个条件),但还没有找到任何方法来执行此操作 我们欢迎您提供任何建议,帮助我们改变现状 DELETE FROM Sales WHERE toDelete='1' 要实现更分区的功能&可能不需要事务日志。我过去必须这

假设我们有30列500000行的表Sales。我想删除表中的400000个(其中
“toDelete='1'

但我有几个限制:

  • 该表是“经常”读/写的,我不希望长时间的“删除”会花费很长时间并锁定该表太长时间
  • 我需要跳过事务日志(如使用
    截断
    ),但在执行
    “删除…其中…”
    (我需要设置一个条件),但还没有找到任何方法来执行此操作
我们欢迎您提供任何建议,帮助我们改变现状

DELETE FROM Sales WHERE toDelete='1'

要实现更分区的功能&可能不需要事务日志。

我过去必须这样做的一种方法是使用一个存储过程或脚本来删除n条记录。重复,直到完成

DELETE TOP 1000 FROM Sales WHERE toDelete='1'

您应该尝试给它一个
ROWLOCK
提示,这样它就不会锁定整个表。但是,如果删除大量行,则会发生锁升级

另外,确保在
toDelete
列上有一个非聚集筛选的索引(仅针对1个值)。如果可能的话,将其设置为bit列,而不是varchar(或现在的名称)

最后,您可以尝试在表上进行迭代并分块删除

已更新

既然循环和块删除在这里是新的粉红色,我也会加入我的版本(结合我以前的答案):


调用
DELETE FROM TableName
将在一个大型事务中完成整个删除。这个很贵

下面是另一个将批量删除行的选项:

deleteMore:
DELETE TOP(10000) Sales WHERE toDelete='1'
IF @@ROWCOUNT != 0
    goto deleteMore

您需要的是批处理

While (select Count(*) from sales where toDelete =1) >0
BEGIN
Delete from sales where SalesID in
(select top 1000 salesId from sales where toDelete = 1)
END

当然,你可以实验哪一个是批量使用的最佳值,我根据表格使用了500-50000。如果您使用cascade delete,您可能需要一个较小的数字,因为您要删除这些子记录。

我自己对该功能的看法如下。 这样就没有重复的代码,您可以管理块大小

DECLARE @DeleteChunk INT = 10000
DECLARE @rowcount INT = 1

WHILE @rowcount > 0
BEGIN

  DELETE TOP (@DeleteChunk) FROM Sales WITH(ROWLOCK)

  SELECT @rowcount = @@RowCount
END

我用下面的方法删除了大约5000万条记录-

BEGIN TRANSACTION     
     DeleteOperation:
     DELETE TOP (BatchSize)
     FROM  [database_name].[database_schema].[database_table] 

     IF @@ROWCOUNT > 0
     GOTO DeleteOperation
COMMIT TRANSACTION

请注意,保持BatchSize<5000在资源上比较便宜。

因为我认为删除大量记录的最佳方法是通过
主键将其删除。(什么是
主键

因此,您必须生成包含要删除的所有行列表的tsql脚本,然后执行该脚本

sqlcmd -S [Instance Name] -E -d [Database] -i [Script]
例如,下面的代码将生成该文件

GO
SET NOCOUNT ON

SELECT   'DELETE FROM  DATA_ACTION WHERE ID = ' + CAST(ID AS VARCHAR(50)) + ';' + CHAR(13) + CHAR(10) + 'GO'
FROM    DATA_ACTION
WHERE  YEAR(AtTime) = 2014
输出文件会有这样的记录

DELETE FROM  DATA_ACTION WHERE ID = 123;
GO
DELETE FROM  DATA_ACTION WHERE ID = 124;
GO
DELETE FROM  DATA_ACTION WHERE ID = 125;
GO
现在您必须使用
SQLCMD
实用程序来执行此脚本

sqlcmd -S [Instance Name] -E -d [Database] -i [Script]

您可以在这里找到解释了的这种方法

我将在这里留下我的答案,因为我能够测试批量删除和更新的不同方法(我必须更新然后删除125+mio行,服务器有16GB的RAM,Xeon E5-2680@2.7GHz,SQL server 2012)

TL;DR:始终按主键更新/删除,绝不按任何其他条件更新/删除。如果您不能直接使用PK,请创建一个临时表并用PK值填充它,然后使用该表更新/删除您的表。为此使用索引

我从解决方案开始(由@Kevin Aenmey编写),但这种方法被证明是不合适的,因为我的数据库是实时的,它每秒处理数百个事务,并且涉及到一些阻塞(有一个针对条件中所有字段的索引,使用
和(ROWLOCK)
没有改变任何内容)

所以,我添加了一条语句,它允许数据库处理其他事务

deleteMore:
WAITFOR DELAY '00:00:01'
DELETE TOP(1000) FROM MyTable WHERE Column1 = @Criteria1 AND Column2 = @Criteria2 AND Column3 = @Criteria3
IF @@ROWCOUNT != 0
    goto deleteMore
这种方法能够处理~1.6mio行/小时的更新和~0,2mio行/小时的删除

转向临时表格改变了很多事情

deleteMore:
SELECT TOP 10000 Id /* Id is the PK */
  INTO #Temp 
  FROM MyTable WHERE Column1 = @Criteria1 AND Column2 = @Criteria2 AND Column3 = @Criteria3 

DELETE MT
  FROM MyTable MT
  JOIN #Temp T ON T.Id = MT.Id 

/* you can use IN operator, it doesn't change anything
 DELETE FROM MyTable WHERE Id IN (SELECT Id FROM #Temp)

 */
IF @@ROWCOUNT > 0 BEGIN
    DROP TABLE #Temp
    WAITFOR DELAY '00:00:01'
    goto deleteMore
END ELSE BEGIN
    DROP TABLE #Temp
    PRINT 'This is the end, my friend'
END

此解决方案处理了约2500万行/小时的更新(快15倍)和约220万行/小时的删除(快11倍)。

当我知道大约有多少次迭代时,我是这样做的:

delete from Activities with(rowlock) where Id in (select top 999 Id from Activities 
(nolock) where description like 'financial data update date%' and len(description) = 87 
and User_Id = 2);
waitfor delay '00:00:02'
GO 20
编辑:对我来说,这比选择top更有效、更快:

declare @counter int = 1
declare @msg varchar(max)
declare @batch int = 499

while ( @counter <= 37600)

begin
    set @msg = ('Iteration count = ' + convert(varchar,@counter))
    raiserror(@msg,0,1) with nowait
    delete Activities with (rowlock) where Id in (select Id from Activities (nolock) where description like 'financial data update date%' and len(description) = 87 and User_Id = 2 order by Id asc offset 1 ROWS fetch next @batch rows only)
    set @counter = @counter + 1
    waitfor delay '00:00:02'
end
declare@counter int=1
声明@msg varchar(最大值)
声明@batch int=499

while(@counter为什么需要跳过事务日志?如果您完成后能发布最佳解决方案(或至少是最适合您的解决方案),我们将不胜感激@thecoon:我肯定会的。感谢大家提供的各种补充答案。请查看恢复模型。您可以中断删除操作,但如果恢复模型已满,则在备份之前,所有删除操作都将保留在日志中(可能是您想要的)。对于读取操作,可以使用(无锁)如果脏读没有问题。每次收缩数据库时,都会有一只小猫死!澄清一下(这有点像黑客),我这样做是因为当时日志目录上的磁盘空间限制,而不是锁定。我想锁定的持续时间是一样的,只是分散得更长:)收缩数据库?不,请不要那样做。希望你的意思是“检查站”或其他什么…@JoeStefanelli,也许是在一个理想的世界里。我们并不是都有无限的数据存储空间,有时如果您删除了90%的数据库,也可以清理数据文件。如果你说的是索引碎片,那就重新索引吧。@Cylindric,然后在你的答案中修正措辞<代码>删除n条记录,然后收缩数据库。重复操作直到完成。
这是个可怕的建议。哦,天哪!我怎么从来没有意识到可以在
DELETE
语句中放入
TOP()
表达式?比我功能相同的答案要简洁得多+先生,祝你好运!坦白地说,我甚至不记得在SQL 2008中可以使用标签。我宁愿看一段时间的陈述<代码>当1=1开始时;删除。。。;如果@@RowCount=0,则中断;结束对于我来说,这对于下一个sql编写器来说更为清晰,因为循环正在发生,而不是找出可怕的GOTO.beauty!哦,我可以很容易地解析一个GOTO。这是我们在英航制造的混乱
declare @counter int = 1
declare @msg varchar(max)
declare @batch int = 499

while ( @counter <= 37600)

begin
    set @msg = ('Iteration count = ' + convert(varchar,@counter))
    raiserror(@msg,0,1) with nowait
    delete Activities with (rowlock) where Id in (select Id from Activities (nolock) where description like 'financial data update date%' and len(description) = 87 and User_Id = 2 order by Id asc offset 1 ROWS fetch next @batch rows only)
    set @counter = @counter + 1
    waitfor delay '00:00:02'
end