调整对 sql server 表的巨大删除操作

vik*_*ana 4 sql-server delete

我正在基于查询对非常大的 sql server 表执行删除操作,如下所述。

delete db.st_table_1
where service_date between(select min(service_date) from stg_table)
                   and (select max(service_date) from stg_table);
Run Code Online (Sandbox Code Playgroud)

stg_table 和 stg_table_1 在 service_date 上没有索引。

这两个表都加载了数百万行数据,删除操作需要很多时间。请求您的建议以提高此查询的性能。

我提到了下面问题中描述的策略,但无法理解如何实施它。

如何在不丢失数据的情况下删除sql server中的大量数据?

请求您对此提出善意的建议。

更新:

select * into db.temp_stg_table_1
from db.stg_table_1
where service_date not between( select min(service_date) from db.stg_table)
                             and (select max(service_date) from db.stg_table);

exec sp_rename 'stg_table_1' , 'stg_table_1_old'

exec sp_rename 'temp_stg_table_1' , 'test_table_1'

drop table stg_table_1_old
Run Code Online (Sandbox Code Playgroud)

如果按照上述逻辑删除数百万条记录如何。任何优点和缺点。

Ran*_*gen 6

根据您的评论进行测试

在 SQL Server 2014 SP3 上测试

stg_table 和 stg_table_1 在 service_date 上没有索引。

这两个表都加载了数百万行数据,删除操作需要很多时间。

数据线

CREATE TABLE dbo.st_table_1( stg_table_1_ID INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
                             service_date datetime2,
                            val  int)
CREATE TABLE dbo.stg_table (stg_table_ID INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
                            service_date datetime2,
                            val  int)
Run Code Online (Sandbox Code Playgroud)

身份字段上的 PK's + 聚集索引。

数据管理语言

INSERT INTO dbo.stg_table WITH(TABLOCK)
(
service_date,val) 
SELECT -- 1M
 DATEADD(S,rownum,GETDATE()),rownum
 FROM
 (SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) as rownum
FROM master.dbo.spt_values spt1
CROSS APPLY master.dbo.spt_values spt2) as sptvalues

INSERT INTO dbo.st_table_1 WITH(TABLOCK)
(
service_date,val) 
SELECT -- 2.5M
 DATEADD(S,rownum,GETDATE()),rownum
 FROM
 (SELECT TOP(2500000) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) as rownum
FROM master.dbo.spt_values spt1
CROSS APPLY master.dbo.spt_values spt2) as sptvalues

INSERT INTO dbo.stg_table WITH(TABLOCK)
(
service_date,val) 
SELECT -- 4M
 DATEADD(S,rownum,GETDATE()),rownum
 FROM
 (SELECT TOP(4000000) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) as rownum
FROM master.dbo.spt_values spt1
CROSS APPLY master.dbo.spt_values spt2) as sptvalues
Run Code Online (Sandbox Code Playgroud)

2.5M 行dbo.st_table_1和 5M 行dbo.stg_table (几乎)所有这些 2.5M 行都将被查询删除,比您的查询少 10 倍以上。

运行您的查询

基本删除语句的实际执行计划

正如预期的那样dbo.stg_table,访问两次以获取流聚合的最大值和最小值。cpu时间&经过/执行时间:

  CPU time = 4906 ms,  elapsed time = 4919 ms.
Run Code Online (Sandbox Code Playgroud)

缺少的索引提示被添加到执行计划中:

CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>]
ON [dbo].[st_table_1] ([service_date])
INCLUDE ([stg_table_1_ID])
Run Code Online (Sandbox Code Playgroud)

但是,当我们添加索引时,会出现一个额外的排序来从这个新添加的索引中删除行:

在此处输入图片说明

计划

并且 cpu 时间/经过时间增加:

   CPU time = 11156 ms,  elapsed time = 11332 ms.
Run Code Online (Sandbox Code Playgroud)

YMMV,但根据我的示例,根据您对数据的评论,它并没有改进查询。

在上创建索引 [dbo].[stg_table]

CREATE NONCLUSTERED INDEX IX_service_date
ON [dbo].[stg_table] ([service_date]);
Run Code Online (Sandbox Code Playgroud)

因此,MAX()andMIN()可以利用新创建的索引仅返回一行而不是完整的聚集索引扫描:

在此处输入图片说明

随着执行时间的改善:

 SQL Server Execution Times:
   CPU time = 2609 ms,  elapsed time = 4028 ms.
Run Code Online (Sandbox Code Playgroud)

执行计划

但这仅基于索引和我自己的示例。继续需要您自担风险。


额外说明

您应该考虑将删除分成单独的批次,这样它就不会填满日志文件,并且不会有一大块失败/成功的删除。

您也可以考虑使用(TABLOCK)so 整个表从一开始就被锁定。

SET STATISTICS IO, TIME ON;
delete dbo.st_table_1 WITH(TABLOCK)
where service_date between(select min(service_date) from stg_table)
                   and (select max(service_date) from stg_table);
Run Code Online (Sandbox Code Playgroud)

更新:SELECT INTO+sp_rename

select * into db.temp_stg_table_1
from db.stg_table_1
where service_date not between( select min(service_date) from db.stg_table)
                             and (select max(service_date) from db.stg_table);

exec sp_rename 'stg_table_1' , 'stg_table_1_old'

exec sp_rename 'temp_stg_table_1' , 'test_table_1'

drop table stg_table_1_old
Run Code Online (Sandbox Code Playgroud)

如果按照上述逻辑删除数百万条记录如何。任何优点和缺点。

除了性能之外,还sp_rename需要一个Sch-M锁来完成,这意味着它必须等待所有其他会话在可以修改表之前释放它们对表的锁。原始表上的任何索引/约束都将消失,您将不得不重新创建它们。

当我对自己的数据运行查询时:

select * into dbo.temp_stg_table_1
from dbo.st_table_1
where service_date not between( select min(service_date) from dbo.stg_table)
                             and (select max(service_date) from dbo.stg_table);
Run Code Online (Sandbox Code Playgroud)

这并不代表您的数据,请记住这一点。

它正在读取所有行以返回 0,这不是最佳的。

在此处输入图片说明

执行时间长:

 SQL Server Execution Times:
   CPU time = 27717 ms,  elapsed time = 10657 ms.
Run Code Online (Sandbox Code Playgroud)

但是,如果没有有关您的数据的更多信息,这并没有真正意义。需要一个查询计划来给出更正确的建议。