Database
 sql >> Base de Dados >  >> RDS >> Database

Metas de linha, parte 3:antijunções


Este post faz parte de uma série de artigos sobre metas de linha. Você pode encontrar as outras partes aqui:
  • Parte 1:definindo e identificando metas de linha
  • Parte 2:Semijunções

Esta parte aborda quando e por que o otimizador introduz uma meta de linha para uma antijunção.

Introdução


Uma anti-junção também é conhecida como anti-semi-junção. Ele retorna cada linha da entrada de junção A para a qual sem correspondência pode ser encontrado na entrada B.

Para uma anti-junção:
  • O otimizador pode adicionar uma meta de linha interna a um aplicar (junção de loops aninhados correlacionados) somente anti-junção .
  • Uma meta de linha não foi adicionada para loops aninhados não correlacionados anti-junção, anti-junção de hash ou anti-junção de mesclagem.
  • Como sempre, qualquer meta de linha é somente adicionada se for menor que a estimativa sem uma meta de linha aplicada.
  • Lado interno redundante TOP cláusulas e DISTINCT/GROUP BY as operações podem ser simplificadas.

Expandindo o primeiro marcador acima, a principal diferença entre aplicar semijunção e aplicar metas de linha antijunção é:
  • Uma aplicação de semijunção sempre apresenta uma meta de linha (desde que seja menor que a estimativa sem a meta).
  • Uma aplicação de antijunção pode apresentar uma meta de linha , mas somente se uma antijunção lógica for transformada em aplicação durante a otimização baseada em custo .

Peço desculpas por essas regras não serem mais simples, mas eu não as fiz. Espero que algumas discussões e exemplos tornem tudo mais claro.


Nenhuma meta de linha anti-junção por padrão


O otimizador assume que as pessoas escrevem uma semi-junção (indiretamente, por exemplo, usando EXISTS ) com a expectativa de que a linha pesquisada será encontrada . Uma meta de linha de aplicação de semijunção está definida pelo otimizador para ajudar a localizar rapidamente a linha correspondente esperada.

Para anti-junção (expresso, por exemplo, usando NOT EXISTS ) a suposição do otimizador é que uma linha correspondente não será encontrada . Uma meta de linha anti-junção de aplicação não foi definida pelo otimizador, porque espera ter que verificar todas as linhas para confirmar que não há correspondência.

Se houver uma linha correspondente, a aplicação anti-junção pode levar mais tempo para localizar essa linha do que se um objetivo de linha tivesse sido usado. No entanto, a antijunção terminará sua busca assim que a correspondência (inesperada) for encontrada.

Aplicar condições de meta de anti-junção de linha


O SQL Server não nos fornece uma maneira de escrever um anti join diretamente, portanto, temos que usar soluções alternativas como NOT EXISTS , NOT IN/ANY/SOME , ou EXCEPT . Cada um desses formulários resulta em uma representação de subconsulta na árvore analisada no início da compilação da consulta. Essa subconsulta é sempre desenrolada em uma aplicação e, em seguida, transformada em uma antijunção lógica quando possível (os detalhes são os mesmos da semijunção discutida na parte 2). Isso tudo acontece antes mesmo de um plano trivial ser considerado.

Para que uma antijunção obtenha uma meta de linha, ela deve entrar otimização baseada em custo como uma anti-junção lógica (o que significa que a transformação de uma aplicação acima deve ter sido bem-sucedida). Em seguida, o otimizador baseado em custo deve optar por implementar a antijunção lógica como um aplicar . Para que isso aconteça, o otimizador deve primeiro escolher explorar a opção de aplicação; então deve selecionar isso como a opção mais barata (para essa parte do plano).

Uma meta de linha antijunção é definida por qualquer uma das regras de otimização baseadas em custo que podem transformar uma junção em uma aplicação. Uma antijunção que entra otimização baseada em custo como uma aplicação (porque a transformação para anti-junção lógica falhou) não tem uma meta de linha aplicada.

O otimizador baseado em custo só explorará e selecionará a opção join-to-apply se houver uma maneira eficiente de localizar quaisquer linhas laterais internas correspondentes (por exemplo, usando um índice). O otimizador não explorará a opção se a subárvore do lado interno da junção não tiver algo útil para o predicado de aplicação "atravar". Isso pode ser um índice, índice temporário (por meio de um spool de índice ansioso) ou outra chave lógica. Adicionar a meta de linha ajuda o otimizador a avaliar o custo da opção de junção para aplicação, uma vez que no máximo uma linha precisa ser localizada.

Observe que uma antijunção de aplicação pode aparecer em um plano de execução sem um objetivo de linha. Isso ocorre quando a transformação inicial de apply para join falha, como é relativamente comum. Quando isso acontece, a antijunção inicia a vida no otimizador baseado em custo como uma aplicação e, portanto, nunca tem uma meta de linha adicionada por uma das regras de junção para aplicação.

É claro que um objetivo de linha também pode ser introduzido no lado interno desta aplicação por meio de um mecanismo diferente (não associado à aplicação), por exemplo, por um operador Top separado.

Para resumir:
  • Uma antijunção só pode obter uma meta de linha durante a otimização baseada em custo (CBO).
  • As regras que convertem uma antijunção em uma aplicação adicionam uma meta de linha.
  • A antijunção deve entrar no CBO como uma junção, não como uma aplicação.
  • Para entrar no CBO como uma junção, as fases anteriores devem poder reescrever a subconsulta como uma junção (por meio de um estágio de aplicação).
  • A CBO só explora a junção para aplicar a transformação em casos promissores.

Exemplo


É um pouco mais complicado demonstrar tudo isso para aplicar anti-junção do que no caso de aplicar semi-junção. As razões para isso serão abordadas na parte 4.

Enquanto isso, aqui está um exemplo do AdventureWorks mostrando como surge um objetivo de anti-junção de aplicação com linha, usando os mesmos sinalizadores de rastreamento não documentados para semi-junção. O sinalizador de rastreamento 8608 é adicionado para mostrar a estrutura de memorando inicial no início da otimização baseada em custo.
SELECT P.ProductID 
FROM Production.Product AS P
WHERE 
    NOT EXISTS 
    (
        SELECT 1
        FROM Production.TransactionHistoryArchive AS THA 
        WHERE THA.ProductID = P.ProductID
 
        UNION ALL
 
        SELECT 1
        FROM Production.TransactionHistory AS TH 
        WHERE TH.ProductID = P.ProductID
    )
OPTION (QUERYTRACEON 3604, QUERYTRACEON 8607, QUERYTRACEON 8608, QUERYTRACEON 8612, QUERYTRACEON 8621);

A subconsulta existe primeiro é transformada em uma aplicação: