2

我有一些在 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 使用特定的执行计划,因为索引提示通常会适得其反。

更新

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

1 回答 1

0

没有更多信息很难说,但你可以测试一下。有时,获取要插入表变量 (@PKIDs) 的 PK ID 的输出可能会更快,然后在更新语句中使用“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
于 2013-05-17T18:59:19.917 回答