我有一些在 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 使用特定的执行计划,因为索引提示通常会适得其反。
更新
- 重新计算表统计信息没有帮助
- 从左连接更改为“不存在的地方”不会影响所用索引的选择,但它确实稍微改变了计划。而不是哈希匹配(左连接),它现在正在做哈希匹配(左反半连接),这可能更快。