SQL Server:奇怪的执行计划

时间:2013-04-18 04:07:23

标签: sql sql-server sql-server-2008

我在SQL Server 2008 Standard上运行了一些ETL代码。处理相对较少的行(~50,000)并将其加载到临时表中。然后我执行一个插入查询来复制更大表中不存在的行(~1,000,000 +行)。临时表包含与目标表相同的主键和聚簇索引。

create table #NewClaims(ExtractDate datetime, SiteName nvarchar(50), SiteCd nvarchar(50), ContractTypeCd nvarchar(50), 
ClaimRateType nvarchar(50), ClaimRateTypeCd nvarchar(50), ClaimStatus nvarchar(50), ClaimStatusCd nvarchar(50), 
CreationDt datetime, StatusDt datetime, ClaimID nvarchar(50), SeqNum int, CreationUserID nvarchar(50), 
SpecialClaimInd nvarchar(50), JobSeekerID nvarchar(50), InvoiceNum nvarchar(50), JobID nvarchar(50), JobRefId int,
RecoveryReason nvarchar(50), ClaimAmount money, GSTAmount money, ApprovedAmount money, ClaimCurrencyInd nvarchar(50), 
EmployerID nvarchar(50), BaseRateType nvarchar(50), BaseRateTypeCd nvarchar(50)
constraint PK_NewClaims primary key clustered(ClaimID, ClaimStatusCD));

这是将临时表记录加载到目标

的SQL
insert into dbo.Claim(
ExtractDate, SiteName, SiteCd, ContractTypeCd, ClaimRateType, ClaimRateTypeCd, ClaimStatus, ClaimStatusCd, CreationDt, 
StatusDt, ClaimID, SeqNum, CreationUserID, SpecialClaimInd, JobSeekerID, InvoiceNum, JobID, JobSeqNum, RecoveryReason, 
ClaimAmount, GSTAmount, ApprovedAmount, ClaimCurrencyInd, EmployerID, BaseRateType, BaseRateTypeCd
)
select 
n.ExtractDate,  n.SiteName, n.SiteCd, n.ContractTypeCd, n.ClaimRateType, n.ClaimRateTypeCd, n.ClaimStatus, n.ClaimStatusCd, 
n.CreationDt, n.StatusDt, n.ClaimID, n.SeqNum, n.CreationUserID, n.SpecialClaimInd, n.JobSeekerID, n.InvoiceNum, n.JobID, 
n.JobRefId, n.RecoveryReason, n.ClaimAmount, n.GSTAmount, n.ApprovedAmount, n.ClaimCurrencyInd, n.EmployerID, n.BaseRateType, 
n.BaseRateTypeCd
from #NewClaims as n
left join dbo.Claim as c on n.ClaimID = c.ClaimID and n.ClaimStatusCd = c.ClaimStatusCd
where c.ClaimID is null

当我执行此操作时,执行计划会执行一些不寻常的操作。它拒绝在dest表上使用PK聚簇索引,并尝试使用任何其他索引。奇怪的是,它使用此索引来修复ClaimID和StatusCd。如果我逐个禁用dest表上的索引,执行计划将继续尝试使用其他索引,直到我禁用了除群集之外的所有索引,此时它最终放弃并使用它,但产生了一堆位图操作。发生这种情况时,查询运行得更快。

我还尝试添加索引提示:with(index(1))。此提示使其按预期工作,使用索引并运行比非提示版本快得多。强制索引查找在执行计划中显示标量运算符 - 这是否表示存在问题?

Seek Keys[1]: Prefix: [ESD4].[dbo].[Claim].ClaimID, [ESD4].[dbo].[Claim].ClaimStatusCd = Scalar Operator([tempdb].[dbo].[#NewClaims].[ClaimID] as [n].[ClaimID]), Scalar Operator([tempdb].[dbo].[#NewClaims].[ClaimStatusCd] as [n].[ClaimStatusCd])

有什么我想念的吗?我不喜欢强制SQL使用特定的执行计划,因为索引提示通常会适得其反。

更新

  • 重新计算表统计信息没有帮助
  • 从左连接更改为“不存在的位置”不会影响所使用的索引的选择,但它确实稍微改变了计划。它不是哈希匹配(左连接),而是正在进行哈希匹配(左反半连接),这可能更快。

1 个答案:

答案 0 :(得分:0)

没有更多信息很难说,但你可以测试一下。有时,获取要插入表变量(@PKIDs)的PK ID的输出可能会更快,然后在update语句中,使用'select ... from #newClaims inner join @PKIDs'。没有计划和拥有数据集很难说,但也许在你的情况下它可能有所帮助。如果是内连接,则可以使用PK的索引提示。 #NewClaims表也可能只需要在ClaimID和ClaimStatusCd上使用PK,从而减少了构建#NewClaims的时间。

declare @PKIDs table (ClaimID INT PRIMARY KEY)

insert into @PKIDs (ClaimID)
select distinct n.ClaimID
from #NewClaims as n
where not exists (select 1 from dbo.Claim as c where c.ClaimID = n.ClaimID and n.ClaimStatusCd = c.ClaimStatusCd)

insert into dbo.Claim(
ExtractDate, SiteName, SiteCd, ContractTypeCd, ClaimRateType, ClaimRateTypeCd, ClaimStatus, ClaimStatusCd, CreationDt, 
StatusDt, ClaimID, SeqNum, CreationUserID, SpecialClaimInd, JobSeekerID, InvoiceNum, JobID, JobSeqNum, RecoveryReason, 
ClaimAmount, GSTAmount, ApprovedAmount, ClaimCurrencyInd, EmployerID, BaseRateType, BaseRateTypeCd
)
select 
n.ExtractDate,  n.SiteName, n.SiteCd, n.ContractTypeCd, n.ClaimRateType, n.ClaimRateTypeCd, n.ClaimStatus, n.ClaimStatusCd, 
n.CreationDt, n.StatusDt, n.ClaimID, n.SeqNum, n.CreationUserID, n.SpecialClaimInd, n.JobSeekerID, n.InvoiceNum, n.JobID, 
n.JobRefId, n.RecoveryReason, n.ClaimAmount, n.GSTAmount, n.ApprovedAmount, n.ClaimCurrencyInd, n.EmployerID, n.BaseRateType, 
n.BaseRateTypeCd
from #NewClaims as n
join @PKIDs as pkids on pkids.ClaimID = n.ClaimID