Quando executo o código a seguir, leva 22,5 minutos e faz 106 milhões de leituras. No entanto, se eu executar apenas a instrução de seleção interna por si só, levará apenas 15 segundos e fará 264k leituras. Como observação, a consulta de seleção não retorna nenhum registro.
Alguma ideia de por que o IF EXISTS
faria funcionar por muito mais tempo e fazer muito mais leituras? Eu também mudei a instrução select para fazer SELECT TOP 1 [dlc].[id]
e a matei após 2 minutos.
Como uma correção temporária, alterei para fazer um count(*) e atribuir esse valor a uma variável @cnt
. Em seguida, ele faz uma IF 0 <> @cnt
declaração. Mas eu pensei EXISTS
que seria melhor, porque se houvesse registros retornados na instrução select, ele pararia de executar o scan/seeks assim que encontrasse pelo menos um registro, enquanto o count(*)
concluiria a consulta completa. o que estou perdendo?
IF EXISTS
(SELECT [dlc].[ID]
FROM TableDLC [dlc]
JOIN TableD [d]
ON [d].[ID] = [dlc].[ID]
JOIN TableC [c]
ON [c].[ID] = [d].[ID2]
WHERE [c].[Name] <> [dlc].[Name])
BEGIN
<do something>
END
Como expliquei em minha resposta a esta pergunta relacionada:
Como (e por que) o TOP impacta um plano de execução?
Usando
EXISTS
introduz uma meta de linha, onde o otimizador produz um plano de execução destinado a localizar a primeira linha rapidamente. Ao fazer isso, ele assume que os dados são distribuídos uniformemente. Por exemplo, se as estatísticas mostrarem que há 100 correspondências esperadas em 100.000 linhas, ele assumirá que terá que ler apenas 1.000 linhas para encontrar a primeira correspondência.Isso resultará em tempos de execução mais longos do que o esperado se essa suposição for incorreta. Por exemplo, se o SQL Server escolher um método de acesso (por exemplo, varredura não ordenada) que localize o primeiro valor correspondente muito tarde na pesquisa, isso poderá resultar em uma varredura quase completa. Por outro lado, se uma linha correspondente for encontrada entre as primeiras linhas, o desempenho será muito bom. Este é o risco fundamental com metas de linha - desempenho inconsistente.
Geralmente é possível reformular a consulta de forma que um objetivo de linha não seja atribuído. Sem o objetivo da linha, a consulta ainda pode terminar quando a primeira linha correspondente for encontrada (se escrita corretamente), mas a estratégia do plano de execução provavelmente será diferente (e espera-se que seja mais eficaz). Obviamente, count(*) exigirá a leitura de todas as linhas, portanto não é uma alternativa perfeita.
Se você estiver executando o SQL Server 2008 R2 ou posterior, geralmente também poderá usar o sinalizador de rastreamento documentado e compatível 4138 para obter um plano de execução sem uma meta de linha. Este sinalizador também pode ser especificado usando a dica suportada
OPTION (QUERYTRACEON 4138)
, embora esteja ciente de que requer permissão de administrador do sistema em tempo de execução , a menos que seja usado com um guia de plano.Infelizmente
Nenhuma das opções acima é funcional com uma
IF EXISTS
instrução condicional. Aplica-se apenas a DML regular. Funcionará com a formulação alternativaSELECT TOP (1)
que você tentou. Isso pode ser melhor do que usarCOUNT(*)
, que deve contar todas as linhas qualificadas, conforme mencionado anteriormente.Dito isso, há inúmeras maneiras de expressar esse requisito que permitirão evitar ou controlar a meta de linha, encerrando a pesquisa antecipadamente. Um último exemplo:
Como EXISTS só precisa encontrar uma única linha, ele usará um objetivo de linha de um. Às vezes, isso pode produzir um plano abaixo do ideal. Se você espera que seja assim para você, preencha uma variável com o resultado de a
COUNT(*)
e teste essa variável para ver se é maior que 0.Então... Com um objetivo de linha pequeno, ele evitará operações de bloqueio, como construir tabelas de hash ou classificar fluxos que podem ser úteis para junções de mesclagem, porque descobrirá que encontrará algo rapidamente e, portanto, loops aninhados seria melhor se encontrasse algo. Exceto que isso pode tornar um plano muito pior em todo o conjunto. Se encontrar uma única linha fosse rápido, você gostaria deste método para evitar bloqueios...