SE EXISTE demorar mais do que a instrução select incorporada

35

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ó, leva apenas 15 segundos e faz 264k leituras. Como uma observação lateral, a consulta de seleção não retorna registros.

Alguma idéia de por IF EXISTSque isso faria com que ela durasse muito mais tempo e fizesse mais leituras? 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, eu a alterei para fazer uma contagem (*) e atribuir esse valor a uma variável @cnt. Então faz uma IF 0 <> @cntdeclaração. Mas eu pensei EXISTSque seria melhor, porque se houvesse registros retornados na instrução select, ele pararia de executar a verificação / procura uma vez que encontrasse pelo menos um registro, enquanto o count(*)procedimento concluirá 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
Chris Woods
fonte
4
Para evitar o problema do objetivo da linha, outra idéia (não testada, lembre-se!) Pode ser tentar o inverso - IF NOT EXISTS (...) BEGIN END ELSE BEGIN <do something> END.
Aaron Bertrand

Respostas:

32

Alguma idéia de por IF EXISTSque isso faria com que ela durasse muito mais tempo e fizesse mais leituras? Também mudei a instrução select para fazer SELECT TOP 1 [dlc].[id]e a matei após 2 minutos.

Como expliquei na minha resposta a esta pergunta relacionada:

Como (e por que) o TOP afeta um plano de execução?

Usando EXISTS apresenta uma meta de linha, na qual o otimizador produz um plano de execução destinado a localizar a primeira linha rapidamente. Ao fazer isso, assume que os dados são distribuídos uniformemente. Por exemplo, se as estatísticas mostrarem 100 correspondências esperadas em 100.000 linhas, ele assumirá que precisará 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 estiver com defeito. Por exemplo, se o SQL Server escolher um método de acesso (por exemplo, verificação não ordenada) que localizar o primeiro valor correspondente muito tarde na pesquisa, isso poderá resultar em uma verificação quase completa. Por outro lado, se uma linha correspondente for encontrada entre as primeiras, o desempenho será muito bom. Esse é o risco fundamental das metas de linha - desempenho inconsistente.

Como uma correção temporária, eu a alterei para fazer uma contagem (*) e atribuir esse valor a uma variável

Geralmente, é possível reformular a consulta para que uma meta de linha não seja atribuída. Sem o objetivo da linha, a consulta ainda pode terminar quando a primeira linha correspondente for encontrada (se gravada corretamente), mas a estratégia do plano de execução provavelmente será diferente (e, esperançosamente, 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 suportado 4138 para obter um plano de execução sem uma meta de linha. Esse sinalizador também pode ser especificado usando a dica suportada OPTION (QUERYTRACEON 4138) , embora esteja ciente de que requer permissão sysadmin de tempo de execução , a menos que seja usado com um guia de plano.

Infelizmente

Nenhuma das opções acima é funcional com uma IF EXISTSdeclaração condicional. Aplica-se apenas ao DML regular. Ele vai trabalhar com o suplente SELECT TOP (1)formulação você tentou. Isso pode ser melhor do que usarCOUNT(*) , que precisa contar todas as linhas qualificadas, como mencionado anteriormente.

Dito isso, existem várias maneiras de expressar esse requisito que permitirão evitar ou controlar a meta da linha, ao encerrar a pesquisa antecipadamente. Um último exemplo:

DECLARE @Exists bit;

SELECT @Exists =
    CASE
        WHEN 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]
        )
        THEN CONVERT(bit, 1)
        ELSE CONVERT(bit, 0)
    END
OPTION (QUERYTRACEON 4138);

IF @Exists = 1
BEGIN
    ...
END;
Paul White diz que a GoFundMonica
fonte
O exemplo alt que você forneceu foi executado em 3,75 minutos e executou 46m de leitura. Portanto, embora seja mais rápido que minha consulta original, acho que, neste caso, continuarei com o @cnt = count (*) e avaliarei a variável posteriormente. Especialmente desde que 99% do tempo é executado, não haverá nada nele. Parece que, com base nas respostas de você e de Rob, o Existe existe apenas se você realmente espera algum tipo de resultado e esse resultado é distribuído igualmente nos seus dados.
Chris Woods,
3
@ ChrisWoods: Você disse: "Especialmente porque 99% das vezes isso ocorre, não haverá nada nele". Isso praticamente garante que o objetivo da linha de uma é uma péssima idéia, pois você espera que normalmente não haja linhas e precise verificar tudo para descobrir que não há nenhuma. Se você não pode adicionar um índice inteligente, use COUNT (*).
precisa
25

Como EXISTS só precisa encontrar uma única linha, ele usará uma meta de linha de uma. Às vezes, isso pode produzir um plano abaixo do ideal. Se você espera que seja assim, preencha uma variável com o resultado de umCOUNT(*) e teste-a para ver se é maior que 0.

Portanto ... Com um objetivo de linha pequena, evitará operações de bloqueio, como a criação de tabelas de hash ou a classificação de fluxos que podem ser úteis para unir junções, porque isso significa que ele deve encontrar algo rapidamente e, portanto, loops aninhados Seria melhor se encontrasse algo. Só que isso pode fazer um plano muito pior em todo o conjunto. Se encontrar uma única linha fosse rápida, você gostaria desse método para evitar bloqueios ...

Rob Farley
fonte