Eu tenho uma atualização/consulta bastante simples, que tem me causado muita dor ao longo dos anos.
na forma mais simples é:
update VillageSemaphore
set TimeStamp = getdate()
where VillageID in (@X, @Y)
No entanto, em alguns processos armazenados, a consulta também inclui esta subconsulta "OR VillageID in (...)"
update VillageSemaphore
set TimeStamp = getdate()
where VillageID in (@X, @Y)
OR VillageID in ( -- this subquery can return many rows, many different VillageIDs
select VSU.SupportingVillageID
from VillageSupportUnits VSU
where SupportedVillageID = @Z
and VSU.UnitCount <> 0
)
Observe que este OR pode retornar muitos IDs de vila, não apenas um, @Z. Esta versão da consulta, às vezes, é executada por muito tempo. Nenhuma reconstrução de índice, a reconstrução de estatísticas ajuda. Ele é executado lentamente quando o conteúdo da tabela Villages é excluído e repovoado. Nesse caso, a contagem de linhas seria de apenas algumas centenas de linhas. Eu nunca descobri por que isso acontece e sempre vivi com isso.
No entanto, recentemente eu estava olhando para o plano de consulta:
Parece que o número estimado de linhas (4000) é enorme em comparação com o número real de linhas (2).
Eu criei esta estatística, mas não ajuda
CREATE STATISTICS [stat_x] ON [VillageSU]([UnitCount], [VillageID])
ENTÃO MINHA PERGUNTA : alguma sugestão de por que isso poderia ser e o que eu poderia fazer para melhorar isso?
para referência, a tabela se parece com isso:
CREATE TABLE VillageSemaphore(
VillageID int NOT NULL,
TimeStamp datetime NOT NULL,
CONSTRAINT PK97 PRIMARY KEY CLUSTERED (VillageID)
)
ATUALIZAÇÃO: Experimentando esta versão da consulta conforme sugerido por srutzky
CREATE TABLE #VillagesToLock (VillageID INT NOT NULL);
insert into #VillagesToLock values (@X)
insert into #VillagesToLock values (@Y)
insert into #VillagesToLock select VSU.SupportingVillageID
from VillageSupportUnits VSU
where SupportedVillageID = @Z
and VSU.UnitCount <> 0
update VillageSemaphore set TimeStamp = getdate()
where VillageID in (select VillageID from #VillagesToLock)
este é o resultado até agora: http://screencast.com/t/96KafTPoNGM - o plano de consulta parece melhor.
O custo da consulta também caiu de 3% para 1%, o que parece bom. 3% pode não parecer muito, mas este é um procedimento armazenado de 2500 linhas!
PERGUNTA : Não posso tornar #VillagesToLock.VillageID um PK, pois não é exclusivo. E espero que o #VillagesToLock normalmente não tenha mais do que 2 a 10 linhas. VillageSemaphore pode ter muitos milhares de linhas. Vale a pena colocar um índice em #VillagesToLock neste caso?
ATUALIZAÇÃO 24 DE NOVEMBRO Eu implementei esta alternativa
O plano de consulta parece muito melhor
Obrigado a todos que também o tempo para me ajudar!
Embora eu não esteja convencido de que isso é um problema com a própria consulta (você verificou o bloqueio quando ela está lenta? você verificou o(s) tipo(s) de espera ocorrendo durante a execução)
IN
eOR
pode ser um padrão problemático para otimizar para . Você já pensou em dividir isso em várias declarações?Isso pode resolver o problema de estimativa, mas concordo com Max, uma estatística com uma coluna inicial de
UnitCount
não ajudará nas estimativas para essas consultas de qualquer maneira.Como alternativa, você também pode criar uma tabela temporária local de forma que o UPDATE use um
INNER JOIN
:ATUALIZAR:
Acabei de pensar em algo que pode ajudar a tornar a filtragem de duplicatas mais eficiente: que tal usar a
IGNORE_DUP_KEY
configuração no PK? Por exemplo:Se você fizer isso, o seguinte funcionará conforme desejado:
Retorna:
E isso significa que você pode fazer as
INSERT
instruções sugeridas acima sem precisar adicionarDISTINCT
ou fazer qualquer consulta secundária para remover duplicatas :-).Você tem uma chave/índice no SUVillageID no VillageSU? Se não, você vai querer adicionar isso. Além disso, você já tentou isso:
nota: TimeStamp é uma palavra-chave reservada no Access e também pode estar no servidor SQL.
ou
e quanto a