Nas consultas abaixo, estima-se que ambos os planos de execução executem 1.000 buscas em um índice exclusivo.
As buscas são conduzidas por uma varredura ordenada na mesma tabela de origem, portanto, aparentemente, eles devem procurar os mesmos valores na mesma ordem.
Os dois loops aninhados têm <NestedLoops Optimized="false" WithOrderedPrefetch="true">
Alguém sabe por que essa tarefa custa 0,172434 no primeiro plano, mas 3,01702 no segundo?
(O motivo da pergunta é que a primeira consulta foi sugerida para mim como uma otimização devido ao aparente custo do plano muito mais baixo. Na verdade, parece-me que faz mais trabalho, mas estou apenas tentando explicar a discrepância. .)
Configuração
CREATE TABLE dbo.Target(KeyCol int PRIMARY KEY, OtherCol char(32) NOT NULL);
CREATE TABLE dbo.Staging(KeyCol int PRIMARY KEY, OtherCol char(32) NOT NULL);
INSERT INTO dbo.Target
SELECT TOP (1000000) ROW_NUMBER() OVER (ORDER BY @@SPID), LEFT(NEWID(),32)
FROM master..spt_values v1,
master..spt_values v2;
INSERT INTO dbo.Staging
SELECT TOP (1000) ROW_NUMBER() OVER (ORDER BY @@SPID), LEFT(NEWID(),32)
FROM master..spt_values v1;
Consulta 1 link "Colar o plano"
WITH T
AS (SELECT *
FROM Target AS T
WHERE T.KeyCol IN (SELECT S.KeyCol
FROM Staging AS S))
MERGE T
USING Staging S
ON ( T.KeyCol = S.KeyCol )
WHEN NOT MATCHED THEN
INSERT ( KeyCol, OtherCol )
VALUES(S.KeyCol, S.OtherCol )
WHEN MATCHED AND T.OtherCol > S.OtherCol THEN
UPDATE SET T.OtherCol = S.OtherCol;
Consulta 2: link "Colar o plano"
MERGE Target T
USING Staging S
ON ( T.KeyCol = S.KeyCol )
WHEN NOT MATCHED THEN
INSERT ( KeyCol, OtherCol )
VALUES( S.KeyCol, S.OtherCol )
WHEN MATCHED AND T.OtherCol > S.OtherCol THEN
UPDATE SET T.OtherCol = S.OtherCol;
Consulta 1
Consulta 2
O acima foi testado no SQL Server 2014 (SP2) (KB3171021) - 12.0.5000.0 (X64)
@Joe Obbish aponta nos comentários que uma reprodução mais simples seria
SELECT *
FROM staging AS S
LEFT OUTER JOIN Target AS T
ON T.KeyCol = S.KeyCol;
vs
SELECT *
FROM staging AS S
LEFT OUTER JOIN (SELECT * FROM Target) AS T
ON T.KeyCol = S.KeyCol;
Para a tabela intermediária de 1.000 linhas, as duas opções anteriores ainda têm a mesma forma do plano com loops aninhados e o plano sem a tabela derivada parecer mais barata, mas para uma tabela intermediária de 10.000 linhas e a mesma tabela de destino acima da diferença de custos, o plano é alterado O formato (com uma junção de varredura e mesclagem completa parecendo relativamente mais atraente do que as buscas caras) mostra essa discrepância de custo pode ter implicações além de dificultar a comparação de planos.