Se eu tiver a seguinte consulta no banco de dados Stack Overflow2010
UPDATE dbo.Posts
SET Title =
CASE
WHEN CreationDate <= '2008-01-01T00:00:00'
THEN 'A'
ELSE 'B'
END
FROM dbo.Posts
A saída resumida STATISTICS IO
está abaixo
Table 'Posts'. Scan count 1, logical reads 445699, physical reads 375822, page server reads 0, read-ahead reads 445521, page server read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob page server reads 0, lob read-ahead reads 0, lob page server read-ahead reads 0.
(1878895 rows affected)
e o plano de execução está aqui
Se eu criar uma tabela para armazenar o valor que desejo usar na minha comparação:
CREATE TABLE dbo.Canary
(
TheDate DATETIME
)
INSERT INTO dbo.Canary VALUES ('2008-01-01T00:00:00')
e então se eu alterar a consulta da seguinte forma:
UPDATE dbo.Posts
SET Title =
CASE
WHEN CreationDate <= Canary.TheDate
THEN 'A'
ELSE 'B'
END
FROM dbo.Posts
CROSS JOIN dbo.Canary
A saída STATISTICS IO é
Table 'Canary'. Scan count 1, logical reads 1, physical reads 1, page server reads 0, read-ahead reads 0, page server read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob page server reads 0, lob read-ahead reads 0, lob page server read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, page server reads 0, read-ahead reads 0, page server read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob page server reads 0, lob read-ahead reads 0, lob page server read-ahead reads 0.
Table 'Posts'. Scan count 1, logical reads 16787757, physical reads 3127, page server reads 0, read-ahead reads 784795, page server read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob page server reads 0, lob read-ahead reads 0, lob page server read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, page server reads 0, read-ahead reads 6291, page server read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob page server reads 0, lob read-ahead reads 0, lob page server read-ahead reads 0.
(1878895 rows affected)
e o plano de execução está aqui
Podemos ver que o número de leituras lógicas aumentou massivamente, de 445k para 16m. Levei algum tempo para encontrar no plano de execução onde está a fonte disso, mas eu o rastreei usando a propriedade Estatísticas de E/S reais / Leituras lógicas reais e posso ver que as leituras extras estão no operador de atualização de índice clusterizado, também posso ver que esse operador agora tem um valor para Número real de linhas para todas as execuções, enquanto o plano para a primeira consulta não tem.
O que está acontecendo aqui? O que está acontecendo na atualização do índice clusterizado que está causando o aumento nas leituras?
Acredito que a consulta deve usar uma variável se esse padrão de "valor em uma tabela de configuração" for usado. No entanto, esta é uma consulta de um aplicativo de fornecedor, então quero dar um feedback sobre o que está acontecendo como resultado da consulta ter sido escrita da maneira que foi.
Ao adicionar o,
CROSS JOIN dbo.Canary
você não obtém mais o benefício do compartilhamento de conjuntos de linhas e oUPDATE
operador precisa localizar cada linha a ser atualizada, em vez de apenas usar a posição da varredura na parte de leitura do plano.Essa alteração na consulta pode significar que há muitas linhas no lado de leitura que precisam ser reduzidas a uma para que
UPDATE
um agregado de fluxo com oANY
agregado seja adicionado ao plano.Presumo que a adição do agregado de fluxo intermediário é o que impede o compartilhamento do conjunto de linhas neste caso.
Se você remover o agregado (por exemplo, com o abaixo ou adicionando um índice exclusivo
Canary
e realizando uma busca exclusiva), o compartilhamento do conjunto de linhas acontece novamente.Para ver em um ambiente de não produção se o plano de execução usa compartilhamento de conjunto de linhas, você pode usar um traceflag não documentado (
DBCC TRACEON (8666)
) e observar as propriedades doUPDATE
operador no plano de execução.( Edição: Não tenho o banco de dados StackOverflow instalado, então criei uma
Posts
tabela vazia para o acima. Não percebi que você havia postado planos de execução e veja que no seu caso o colapso de muitos para um é feito por um operador de classificação distinto intermediário. Imagino que aTOP 1
abordagem deva se livrar disso também. Outro aspecto do seu plano de problema é que ele é paralelo. Se ele ainda permanecer paralelo, talvez seja necessário adicionar umaMAXDOP 1
dica, pois isso também significará que as linhas não estão mais sendo transmitidas diretamente da varredura para a atualização, uma por uma, e impedirá o compartilhamento do conjunto de linhas.)