Eu criei o comando SQL que usa INNER JOIN em 9 tabelas, de qualquer forma, esse comando leva muito tempo (mais de cinco minutos). Então, meu pessoal sugeriu que eu mudasse INNER JOIN para LEFT JOIN porque o desempenho de LEFT JOIN é melhor, apesar do que eu sei. Depois que eu mudei, a velocidade da consulta melhorou significativamente.
Gostaria de saber por que o LEFT JOIN é mais rápido que o INNER JOIN?
Meu comando SQL se parece abaixo:
SELECT * FROM A INNER JOIN B ON ... INNER JOIN C ON ... INNER JOIN D
e assim por diante
Atualização: Este é um resumo do meu esquema.
FROM sidisaleshdrmly a -- NOT HAVE PK AND FK
INNER JOIN sidisalesdetmly b -- THIS TABLE ALSO HAVE NO PK AND FK
ON a.CompanyCd = b.CompanyCd
AND a.SPRNo = b.SPRNo
AND a.SuffixNo = b.SuffixNo
AND a.dnno = b.dnno
INNER JOIN exFSlipDet h -- PK = CompanyCd, FSlipNo, FSlipSuffix, FSlipLine
ON a.CompanyCd = h.CompanyCd
AND a.sprno = h.AcctSPRNo
INNER JOIN exFSlipHdr c -- PK = CompanyCd, FSlipNo, FSlipSuffix
ON c.CompanyCd = h.CompanyCd
AND c.FSlipNo = h.FSlipNo
AND c.FSlipSuffix = h.FSlipSuffix
INNER JOIN coMappingExpParty d -- NO PK AND FK
ON c.CompanyCd = d.CompanyCd
AND c.CountryCd = d.CountryCd
INNER JOIN coProduct e -- PK = CompanyCd, ProductSalesCd
ON b.CompanyCd = e.CompanyCd
AND b.ProductSalesCd = e.ProductSalesCd
LEFT JOIN coUOM i -- PK = UOMId
ON h.UOMId = i.UOMId
INNER JOIN coProductOldInformation j -- PK = CompanyCd, BFStatus, SpecCd
ON a.CompanyCd = j.CompanyCd
AND b.BFStatus = j.BFStatus
AND b.ProductSalesCd = j.ProductSalesCd
INNER JOIN coProductGroup1 g1 -- PK = CompanyCd, ProductCategoryCd, UsedDepartment, ProductGroup1Cd
ON e.ProductGroup1Cd = g1.ProductGroup1Cd
INNER JOIN coProductGroup2 g2 -- PK = CompanyCd, ProductCategoryCd, UsedDepartment, ProductGroup2Cd
ON e.ProductGroup1Cd = g2.ProductGroup1Cd
sql
sql-server
performance
Anônimo
fonte
fonte
coUOM
? Caso contrário, você poderá usar uma semi-junção. Se sim, você seria capaz de usarUNION
como uma alternativa. A publicação apenas de suaFROM
cláusula é uma informação inadequada aqui.Respostas:
A
LEFT JOIN
não é absolutamente mais rápido que umINNER JOIN
. De fato, é mais lento; por definição, uma junção externa (LEFT JOIN
ouRIGHT JOIN
) deve fazer todo o trabalho de umINNER JOIN
mais o trabalho extra de estender nulos os resultados. Também seria esperado que retornasse mais linhas, aumentando ainda mais o tempo total de execução simplesmente devido ao tamanho maior do conjunto de resultados.(E mesmo que um
LEFT JOIN
fosse mais rápido em situações específicas devido a uma confluência de fatores difícil de imaginar, ele não é funcionalmente equivalente a umINNER JOIN
, portanto, você não pode simplesmente substituir todas as instâncias de uma pela outra!)Provavelmente, seus problemas de desempenho estão em outro lugar, como não ter uma chave candidata ou chave estrangeira indexada corretamente. 9 mesas é bastante para se juntar, então a desaceleração pode literalmente estar quase em qualquer lugar. Se você postar seu esquema, poderemos fornecer mais detalhes.
Editar:
Refletindo ainda mais sobre isso, pude pensar em uma circunstância sob a qual a
LEFT JOIN
pode ser mais rápida que umaINNER JOIN
, e é quando:Considere este exemplo:
Se você executar isso e visualizar o plano de execução, verá que a
INNER JOIN
consulta realmente custa mais que aLEFT JOIN
, porque atende aos dois critérios acima. Isso ocorre porque o SQL Server deseja fazer uma correspondência de hash para oINNER JOIN
, mas faz loops aninhados para oLEFT JOIN
; o primeiro é normalmente muito mais rápido, mas como o número de linhas é muito pequeno e não há índice a ser usado, a operação de hash acaba sendo a parte mais cara da consulta.Você pode ver o mesmo efeito escrevendo um programa em sua linguagem de programação favorita para executar um grande número de pesquisas em uma lista com 5 elementos, em comparação a uma tabela de hash com 5 elementos. Devido ao tamanho, a versão da tabela de hash é realmente mais lenta. Mas aumente para 50 elementos ou 5000 elementos, e a versão da lista diminui para um rastreamento, porque é O (N) vs. O (1) para a hashtable.
Mas mude esta consulta para que esteja na
ID
coluna e não,Name
e você verá uma história muito diferente. Nesse caso, ele faz loops aninhados para as duas consultas, mas aINNER JOIN
versão é capaz de substituir uma das verificações de índice em cluster por uma busca - o que significa que isso literalmente será uma ordem de magnitude mais rápida com um grande número de linhas.Portanto, a conclusão é mais ou menos o que mencionei vários parágrafos acima; isso é quase certamente um problema de indexação ou cobertura de índice, possivelmente combinado com uma ou mais tabelas muito pequenas. Essas são as únicas circunstâncias nas quais o SQL Server às vezes pode escolher um plano de execução pior para um
INNER JOIN
que para umLEFT JOIN
.fonte
Há um cenário importante que pode fazer com que uma junção externa seja mais rápida que uma junção interna que ainda não foi discutida.
Ao usar uma junção externa, o otimizador sempre estará livre para eliminar a tabela de junção externa do plano de execução se as colunas de junção forem a PK da tabela externa e nenhuma das colunas da tabela externa for referenciada fora da própria junção externa. Por exemplo,
SELECT A.* FROM A LEFT OUTER JOIN B ON A.KEY=B.KEY
B.KEY é o PK de B. Tanto o Oracle (acredito que estava usando o release 10) quanto o Sql Server (usei 2008 R2) retiram a tabela B do plano de execução.O mesmo não é necessariamente verdadeiro para uma junção interna:
SELECT A.* FROM A INNER JOIN B ON A.KEY=B.KEY
pode ou não exigir B no plano de execução, dependendo de quais restrições existem.Se A.KEY for uma chave estrangeira anulável que faça referência a B.KEY, o otimizador não poderá excluir B do plano porque deve confirmar que existe uma linha B para cada linha A.
Se A.KEY é uma chave estrangeira obrigatória que faz referência a B.KEY, o otimizador pode liberar B do plano porque as restrições garantem a existência da linha. Mas apenas porque o otimizador pode descartar a tabela do plano, não significa que sim. O SQL Server 2008 R2 NÃO elimina B do plano. O Oracle 10 exclui B do plano. É fácil ver como a associação externa superará a associação interna no SQL Server nesse caso.
Este é um exemplo trivial e não é prático para uma consulta independente. Por que participar de uma mesa se você não precisa?
Mas isso pode ser uma consideração de design muito importante ao projetar vistas. Freqüentemente é criada uma visualização "faça tudo" que une tudo o que um usuário pode precisar relacionado a uma tabela central. (Especialmente se houver usuários ingênuos fazendo consultas ad-hoc que não entendem o modelo relacional) A exibição pode incluir todas as colunas relevantes de muitas tabelas. Mas os usuários finais podem acessar apenas colunas de um subconjunto das tabelas na visualização. Se as tabelas forem unidas a junções externas, o otimizador poderá (e fará) eliminar as tabelas desnecessárias do plano.
É fundamental garantir que a exibição usando junções externas ofereça os resultados corretos. Como Aaronaught disse - você não pode substituir cegamente OUTER JOIN por INNER JOIN e esperar os mesmos resultados. Mas há momentos em que pode ser útil por razões de desempenho ao usar visualizações.
Uma última observação - não testei o impacto no desempenho à luz do exposto acima, mas, em teoria, parece que você poderá substituir com segurança uma INNER JOIN por uma OUTER JOIN se também adicionar a condição <FOREIGN_KEY> NÃO É NULL para a cláusula where.
fonte
Se tudo funciona como deveria, mas todos sabemos que tudo não funciona da maneira que deveria, especialmente quando se trata do otimizador de consultas, cache do plano de consulta e estatísticas.
Primeiro, sugiro reconstruir o índice e as estatísticas e, em seguida, limpar o cache do plano de consulta apenas para garantir que isso não esteja estragando tudo. No entanto, experimentei problemas mesmo quando isso é feito.
Eu experimentei alguns casos em que uma junção esquerda foi mais rápida que uma junção interna.
O motivo subjacente é o seguinte: se você possui duas tabelas e se junta a uma coluna com um índice (nas duas tabelas). A junção interna produzirá o mesmo resultado, independentemente se você fizer um loop sobre as entradas no índice na tabela um e corresponder ao índice na tabela dois como se você fizesse o inverso: Fazer loop sobre as entradas no índice na tabela dois e corresponder ao índice na tabela um. O problema é que, quando você tem estatísticas enganosas, o otimizador de consulta usa as estatísticas do índice para encontrar a tabela com menos entradas correspondentes (com base em seus outros critérios). Se você possui duas tabelas com 1 milhão em cada uma, na tabela 1 você tem 10 linhas correspondentes e na tabela 2 você tem 100000 linhas correspondentes. A melhor maneira seria fazer uma varredura de índice na tabela um e corresponder 10 vezes na tabela dois. O inverso seria uma varredura de índice que repetisse mais de 100000 linhas e tentasse corresponder 100000 vezes e apenas 10 fossem bem-sucedidas. Portanto, se as estatísticas não estiverem corretas, o otimizador poderá escolher a tabela e o índice incorretos a serem repetidos.
Se o otimizador optar por otimizar a junção esquerda na ordem em que foi gravado, ele terá um desempenho melhor que a junção interna.
MAS, o otimizador também pode otimizar uma junção esquerda sub-idealmente como uma semi-junção esquerda. Para escolher a que você deseja, use a dica de ordem de força.
fonte
Tente as duas consultas (aquela com junção interna e esquerda)
OPTION (FORCE ORDER)
no final e publique os resultados.OPTION (FORCE ORDER)
é uma dica de consulta que força o otimizador a criar o plano de execução com a ordem de associação que você forneceu na consulta.Se
INNER JOIN
começa a funcionar tão rápido quantoLEFT JOIN
, é porque:INNER JOIN
s, a ordem de junção não importa. Isso permite que o otimizador de consultas solicite as junções como achar melhor, para que o problema possa depender do otimizador.LEFT JOIN
, não é esse o caso, pois alterar a ordem de junção alterará os resultados da consulta. Isso significa que o mecanismo deve seguir a ordem de junção que você forneceu na consulta, que pode ser melhor que a otimizada.Não sei se isso responde à sua pergunta, mas eu já participei de um projeto que apresentava consultas altamente complexas fazendo cálculos, o que atrapalhava completamente o otimizador. Tivemos casos em
FORCE ORDER
que a reduziria o tempo de execução de uma consulta de 5 minutos para 10 segundos.fonte
Fizeram várias comparações entre as junções externa e interna esquerda e não foram capazes de encontrar uma diferença consistente. Existem muitas variáveis. Estou trabalhando em um banco de dados de relatórios com milhares de tabelas, muitas com um grande número de campos, muitas alterações ao longo do tempo (versões de fornecedores e fluxo de trabalho local). Não é possível criar todas as combinações de índices de cobertura para atender às necessidades de uma ampla variedade de consultas e manipular dados históricos. As consultas internas viram o desempenho do servidor prejudicar porque duas tabelas grandes (milhões a dezenas de milhões de linhas) são unidas internamente, puxando um grande número de campos e não existe um índice de cobertura.
O maior problema, no entanto, não parece mais favorável nas discussões acima. Talvez seu banco de dados seja bem projetado com gatilhos e processamento de transações bem projetado para garantir bons dados. Os meus freqüentemente têm valores NULL onde eles não são esperados. Sim, as definições da tabela podem impor nulos, mas isso não é uma opção no meu ambiente.
Portanto, a questão é ... você projeta sua consulta apenas para velocidade, uma prioridade mais alta para o processamento de transações que executa o mesmo código milhares de vezes por minuto. Ou você busca a precisão que uma junção externa esquerda fornecerá. Lembre-se de que as junções internas devem encontrar correspondências nos dois lados; portanto, um NULL inesperado não apenas removerá os dados das duas tabelas, mas possivelmente linhas inteiras de informações. E isso acontece tão bem, sem mensagens de erro.
Você pode ser muito rápido ao obter 90% dos dados necessários e não descobrir que as junções internas removeram silenciosamente as informações. Às vezes, as junções internas podem ser mais rápidas, mas não acredito que alguém faça essa suposição, a menos que tenha revisado o plano de execução. A velocidade é importante, mas a precisão é mais importante.
fonte
É mais provável que seus problemas de desempenho sejam causados pelo número de associações que você está fazendo e se as colunas nas quais você está ingressando têm índices ou não.
Na pior das hipóteses, você poderia facilmente fazer 9 varreduras inteiras na tabela para cada associação.
fonte
As junções externas podem oferecer desempenho superior quando usadas em visualizações.
Digamos que você tenha uma consulta que envolva uma exibição, e essa exibição seja composta por 10 tabelas unidas. Digamos que sua consulta use apenas colunas de 3 dessas 10 tabelas.
Se essas 10 tabelas tivessem sido unidas internamente, o otimizador de consulta precisaria juntar todas, mesmo que sua consulta em si não precise de 7 das 10 tabelas. Isso ocorre porque as junções internas podem filtrar os dados, tornando-os essenciais para a computação.
Se essas 10 tabelas tivessem sido unidas externamente, o otimizador de consulta apenas se uniria às necessárias: três em cada dez delas, neste caso. Isso ocorre porque as próprias junções não estão mais filtrando os dados e, portanto, as junções não utilizadas podem ser puladas.
Fonte: http://www.sqlservercentral.com/blogs/sql_coach/2010/07/29/poor-little-misunderstood-views/
fonte
Encontrei algo interessante no SQL Server ao verificar se as junções internas são mais rápidas que as esquerdas.
Se você não incluir os itens da tabela de junção esquerda, na instrução select, a junção esquerda será mais rápida que a mesma consulta com junção interna.
Se você incluir a tabela de junção esquerda na instrução select, a junção interna com a mesma consulta foi igual ou mais rápida que a junção esquerda.
fonte
Pelas minhas comparações, acho que eles têm exatamente o mesmo plano de execução. Existem três cenários:
Se e quando eles retornarem os mesmos resultados, eles terão a mesma velocidade. No entanto, devemos ter em mente que elas não são as mesmas consultas e que LEFT JOIN possivelmente retornará mais resultados (quando algumas condições ON não forem atendidas) - é por isso que geralmente é mais lento.
Quando a tabela principal (a primeira não-const no plano de execução) possui uma condição restritiva (WHERE id =?) E a condição ON correspondente está em um valor NULL, a tabela "direita" não é unida --- é quando LEFT JOIN é mais rápido.
Conforme discutido no ponto 1, geralmente INNER JOIN é mais restritivo e retorna menos resultados e, portanto, é mais rápido.
Ambos usam índices (os mesmos).
fonte