2013-04-18 5 views
2

У меня есть код ETL, работающий на SQL Server 2008 Standard. Относительно небольшое количество строк (~ 50 000) обрабатывается и загружается в временную таблицу. Затем я выполняю запрос на вставку для копирования по строкам, которых нет в гораздо более крупной таблице (~ 1,000,000 + rows). Таблица temp содержит тот же первичный ключ и кластеризованный индекс, что и таблица назначения.SQL Server: Странный план выполнения

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 

Когда я запускаю этот план исполнения делает что-то необычное. Он отказывается использовать кластерный индекс PK в таблице dest и пытается использовать любой другой индекс, доступный вместо этого. Любопытно, что он использует этот индекс для восстановления идентификаторов ClaimID и StatusCd. Если я отключу индексы в таблице dest, один за другим, план выполнения будет продолжать пытаться использовать другие индексы, пока я не отключил их всех, кроме кластеризованных, и в этот момент он, наконец, дает и использует его, но создает пучок растровых операций. Запрос выполняется быстрее, когда это происходит.

Я также экспериментировал с добавлением указательного указателя: с (индекс (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 использовать конкретный план выполнения, поскольку подсказки индексов часто могут иметь неприятные последствия.

Update

  • Повторное вычисление статистики таблицы не помогло
  • Изменение слева присоединитесь к «где не существует» не влияет на выбор индекса используется, но он изменить план немного. Вместо хеш-матча (Left Join), теперь он выполняет Hash Match (Left Anti Semi Join), который, предположительно, быстрее.
+0

Ваша статистика обновлена ​​в вашей целевой таблице? – Aushin

+0

Не уверен. Как я могу проверить или обновить их? – Trent

+1

Вы можете обновить их, запустив это в своей таблице. Http://msdn.microsoft.com/en-us/library/ms187348.aspx – Aushin

ответ

0

Трудно сказать без дополнительной информации, но вы можете проверить это. Иногда бывает быстрее получить вывод идентификаторов PK, которые вы хотите вставить в табличную переменную (@PKID), а затем в инструкции обновления использовать «select ... from #newClaims inner join @PKIDs». Трудно сказать без плана и наличия набора данных, но, возможно, в вашей ситуации это может помочь. Если вы используете внутреннее соединение, вы можете использовать индексный указатель для ПК. Таблице #NewClaims также может потребоваться только PK для ClaimID и ClaimStatusCd, что сокращает время на создание #NewClaim.

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 
Смежные вопросы