Sql server 为什么存储过程在简单DELETE语句上意外地获得优化超时?

Sql server 为什么存储过程在简单DELETE语句上意外地获得优化超时?,sql-server,performance,stored-procedures,Sql Server,Performance,Stored Procedures,数据仓库中有一个多步骤过程,它生成一个临时表,其中包含将为每个批处理的作业列表。通常这大约是5000个工作岗位。到财务汇总结束时,我们可能会看到大约500000条已处理的记录。我注意到,它的一小部分让我提前暂停了存储过程的这一部分的优化: DELETE jfs FROM DataWarehouse.dbo.JobFinancialSummary jfs -- Financials table (> 3,000,000 records with indices) IN

数据仓库中有一个多步骤过程,它生成一个临时表,其中包含将为每个批处理的作业列表。通常这大约是5000个工作岗位。到财务汇总结束时,我们可能会看到大约500000条已处理的记录。我注意到,它的一小部分让我提前暂停了存储过程的这一部分的优化:

DELETE jfs    
FROM DataWarehouse.dbo.JobFinancialSummary jfs        -- Financials table (> 3,000,000 records with indices)
 INNER JOIN #JobList jl ON jfs.JobID = jl.JobID       -- List of Jobs being processed (avg. of 5,000 records)
 INNER JOIN FiscalPeriod fp ON fp.ID = jfs.FiscalPeriodID    -- Month Reference Table (about 1,000 records)
WHERE fp.[Status] IN (1,2)   -- Last 2 months

最令人困惑的是,这是存储过程中一个相对简单的部分,所有连接都在索引上。我唯一的问题是,当优化器对此进行评估时,它是如何超时的。我的理解是,优化器为每个语句提供了自己的“预算”,但也许我遗漏了一些东西。为什么这里会超时?

删除速度非常慢的原因有很多:

原因:

  • 表可能包含CDC,启用了CT
  • 表可能包含在delete操作之后工作的触发器
  • 表可能包含FK引用、约束
  • 表可能包含与该表关联的索引视图
  • 首先,我们需要检查此表的事务性等
  • 删除的方法有很多,显然批量删除速度更快。第一种也是最著名的方法是使用软删除属性标记这些记录,并在夜间脱机删除
如果要脱机删除,为了加快删除速度

然后捕获删除表的聚集索引键

  • 禁用索引、FK约束,因为您将在功能上注意所有这些约束
  • 如果不需要,禁用该表上的CT、CDC等 为索引视图创建脚本,然后删除与此表关联的索引视图
  • 然后通过批删除,通过设置@rowCount或top batchsize,可以更快地删除任意数量的记录

    删除前50000名--基于场景 来自表1 循环中

调用显式“检查点”以确保从事务日志中清除记录。还要确保您的“恢复模式”是“简单”而不是“完整”
如果仍在白天进行在线删除,则标记为软删除,并在夜间作业中以非常小的批量运行删除操作

删除速度非常慢的原因有很多:

原因:

  • 表可能包含CDC,启用了CT
  • 表可能包含在delete操作之后工作的触发器
  • 表可能包含FK引用、约束
  • 表可能包含与该表关联的索引视图
  • 首先,我们需要检查此表的事务性等
  • 删除的方法有很多,显然批量删除速度更快。第一种也是最著名的方法是使用软删除属性标记这些记录,并在夜间脱机删除
如果要脱机删除,为了加快删除速度

然后捕获删除表的聚集索引键

  • 禁用索引、FK约束,因为您将在功能上注意所有这些约束
  • 如果不需要,禁用该表上的CT、CDC等 为索引视图创建脚本,然后删除与此表关联的索引视图
  • 然后通过批删除,通过设置@rowCount或top batchsize,可以更快地删除任意数量的记录

    删除前50000名--基于场景 来自表1 循环中

调用显式“检查点”以确保从事务日志中清除记录。还要确保您的“恢复模式”是“简单”而不是“完整”
如果仍在白天进行在线删除,则将其标记为软删除,并在夜间作业中以非常小的批运行删除操作

可能会更快地隔离要先删除的行/条目,而不是在删除时加入

假设
id
是您的主键/标识,则类似于此:

IF OBJECT_ID('tempdb..#tmp') IS NOT NULL DROP TABLE #tmp

SELECT jfs.ID 
INTO #tmp
FROM DataWarehouse.dbo.JobFinancialSummary jfs        -- Financials table (> 3,000,000 records with indices)
INNER JOIN FiscalPeriod fp ON fp.ID = jfs.FiscalPeriodID    -- Month Reference Table (about 1,000 records)
WHERE fp.[Status] IN (1,2)
/*  EXISTS IS FASTER THAN A JOIN, AVOIDS FANNING */
AND EXISTS (SELECT 1 FROM #JobList jl where jfs.JobID = jl.JobID)       -- List of Jobs being processed (avg. of 5,000 records)
然后发出删除命令,例如:

DELETE TOP(1000) jfs
FROM DataWarehouse.dbo.JobFinancialSummary jfs 
WHERE EXISTS (SELECT 1 FROM #tmp t WHERE jfs.ID=t.ID)
从这里开始,根据要删除的行数,您可能希望在一夜之间批量删除—任何超过5000行的内容都将升级为表锁,并且是批量删除的主要候选对象

我在这里写了一个关于如何完成大批量删除的相当流行的答案:


首先隔离要删除的行/条目可能比在删除时加入更快

假设
id
是您的主键/标识,则类似于此:

IF OBJECT_ID('tempdb..#tmp') IS NOT NULL DROP TABLE #tmp

SELECT jfs.ID 
INTO #tmp
FROM DataWarehouse.dbo.JobFinancialSummary jfs        -- Financials table (> 3,000,000 records with indices)
INNER JOIN FiscalPeriod fp ON fp.ID = jfs.FiscalPeriodID    -- Month Reference Table (about 1,000 records)
WHERE fp.[Status] IN (1,2)
/*  EXISTS IS FASTER THAN A JOIN, AVOIDS FANNING */
AND EXISTS (SELECT 1 FROM #JobList jl where jfs.JobID = jl.JobID)       -- List of Jobs being processed (avg. of 5,000 records)
然后发出删除命令,例如:

DELETE TOP(1000) jfs
FROM DataWarehouse.dbo.JobFinancialSummary jfs 
WHERE EXISTS (SELECT 1 FROM #tmp t WHERE jfs.ID=t.ID)
从这里开始,根据要删除的行数,您可能希望在一夜之间批量删除—任何超过5000行的内容都将升级为表锁,并且是批量删除的主要候选对象

我在这里写了一个关于如何完成大批量删除的相当流行的答案:


好的观点。我可以说没有FKs、触发器、索引视图或约束(主键除外)。这是很好的观点。我可以说没有FK、触发器、索引视图或约束(主键除外)。如果提供了这些,这类问题可能会涉及更多内容:
表模式以及计数、涉及的表索引、查询的执行计划
这类问题可能会涉及更多内容,提供时:
表模式以及计数、涉及的表上的索引、查询的执行计划