Condição em JOIN ou WHERE

192

Existe alguma diferença (desempenho, prática recomendada, etc ...) entre colocar uma condição na cláusula JOIN versus a cláusula WHERE?

Por exemplo...

-- Condition in JOIN
SELECT *
FROM dbo.Customers AS CUS
INNER JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
AND CUS.FirstName = 'John'

-- Condition in WHERE
SELECT *
FROM dbo.Customers AS CUS
INNER JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
WHERE CUS.FirstName = 'John'

Qual você prefere (e talvez por que)?

Steve Dignan
fonte
4
Você executou as duas consultas? Você verificou os planos de execução gerados pelas duas consultas? O que você observou?
214/09 S.Lott
21
@ S.Lott, esta consulta é apenas para fins de exemplo. Só estou me perguntando "em geral" qual é o método preferido - se houver.
9788 Steve Dignan
1
@ Steve Dignan: você deve fazer o benchmark com dados de amostra e examinar os planos de consulta. A resposta será muito, muito clara. E - bônus - você terá um código que poderá reutilizar quando surgirem situações mais complexas.
217/09 S.Lott
1
Eu pessoalmente colocaria a condição na cláusula JOIN se a condição descrever a relação. As condições genéricas que apenas filtram o conjunto de resultados seriam então para a parte WHERE. ExemploFROM Orders JOIN OrderParties ON Orders.Id = OrderParties.Order AND OrderParties.Type = 'Recipient' WHERE Orders.Status = 'Canceled'
Glutexo 2/16

Respostas:

153

A álgebra relacional permite a intercambiabilidade dos predicados na WHEREcláusula e do INNER JOIN, portanto, mesmo INNER JOINconsultas com WHEREcláusulas podem ter os predicados reorganizados pelo otimizador para que eles já possam ser excluídos durante oJOIN processo.

Eu recomendo que você escreva as consultas da maneira mais legível possível.

Às vezes, isso inclui tornar o INNER JOINrelativamente "incompleto" e colocar alguns dos critérios noWHERE simplesmente para facilitar a manutenção das listas de critérios de filtragem.

Por exemplo, em vez de:

SELECT *
FROM Customers c
INNER JOIN CustomerAccounts ca
    ON ca.CustomerID = c.CustomerID
    AND c.State = 'NY'
INNER JOIN Accounts a
    ON ca.AccountID = a.AccountID
    AND a.Status = 1

Escrever:

SELECT *
FROM Customers c
INNER JOIN CustomerAccounts ca
    ON ca.CustomerID = c.CustomerID
INNER JOIN Accounts a
    ON ca.AccountID = a.AccountID
WHERE c.State = 'NY'
    AND a.Status = 1

Mas isso depende, é claro.

Cade Roux
fonte
7
Não se trata apenas de consulta ou legibilidade limpas, mas de desempenho. a colocação das condições na junção melhora o desempenho de grande quantidade de dados com tabelas indexadas corretamente.
Shahdat 6/10/19
1
Acabei de executar relatórios de vendas mensais juntando 5-6 tabelas em alguns milhões de registros. O Perf melhora em 30% - sql server 2012
Shahdat 6/16
2
@ Shahdat, se você estiver obtendo uma diferença significativa de desempenho ao mover suas condições de filtro da cláusula where para a junção interna, precisará postar esses planos de execução.
Cade Roux
4
@Cade Investiguei os planos de execução - ambos os cenários mostram o mesmo custo. Eu executo as consultas várias vezes, parece que ambas demoram o mesmo tempo. Anteriormente, eu estava executando as consultas na produção e obtinha uma diferença significativa de desempenho porque o banco de dados estava sendo usado por usuários ativos. Desculpe por essa confusão.
Shahdat 18/10/19
4
Esta resposta é correta para INNER JOINs, mas não para junções esquerda / direita.
SOTN
121

Para junções internas, eu realmente não notei uma diferença (mas, como em todo ajuste de desempenho, você precisa verificar seu banco de dados sob suas condições).

No entanto, onde você coloca a condição, faz uma enorme diferença se você estiver usando junções esquerda ou direita. Por exemplo, considere estas duas consultas:

SELECT *
FROM dbo.Customers AS CUS 
LEFT JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
WHERE ORD.OrderDate >'20090515'

SELECT *
FROM dbo.Customers AS CUS 
LEFT JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
AND ORD.OrderDate >'20090515'

O primeiro fornecerá apenas os registros com um pedido datado depois de 15 de maio de 2009, convertendo assim a junção esquerda em uma junção interna. O segundo fornecerá esses registros, além de quaisquer clientes sem pedidos. O conjunto de resultados é muito diferente dependendo de onde você coloca a condição. (Selecione * se apenas para fins de exemplo, você não deve usar obviamente o código de produção.) A exceção é quando você deseja ver apenas os registros em uma tabela, mas não na outra. Então você usa a cláusula where para a condição e não a junção.

SELECT *
FROM dbo.Customers AS CUS 
LEFT JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
WHERE ORD.OrderID is null
HLGEM
fonte
Obrigado por explicar com exemplos
Rennish Joseph
1
"convertendo assim a junção esquerda em uma junção interna". Quão? Você pode elaborar um pouco?
precisa saber é o seguinte
@ user1451111 Saiba o que retorna LEFT / RIGHT JOIN: linhas INNER JOIN mais linhas da tabela esquerda / direita sem comparação estendidas por NULLs. FULL JOIN retorna INNER JOIN linhas UNION TODAS as linhas da tabela esquerda e direita sem comparação estendidas por NULLs. Sempre saiba o que INNER JOIN você deseja como parte de um OUTTER JOIN. Um WHERE ou ON que requer que uma coluna estendida possivelmente com NULL não seja NULL após uma OUTER JOIN ON remove as linhas estendidas por NULLs, ou seja, deixa apenas as linhas INNER JOIN, ou seja, "transforma uma OUTER JOIN em INNER JOIN".
philipxy
1
@ user1451111 ou, em termos mais simples: A left join Btodas as linhas de A se uniram a todas as linhas correspondentes de B. Se B não tiver nenhuma linha que corresponda, as colunas A terão um valor, mas todas as colunas de B nessa linha serão exibidas como valores NULL. Se você escreveu where B.somecolumn = ‘somevalue’, você tem um NULL (B.somecolumn) sendo comparado com 'somevalue'. Qualquer coisa comparada com NULL é falsa; portanto, todas as suas linhas em que não há linha B correspondente para a linha A são eliminadas e os resultados obtidos são os mesmos que um INNER JOIN daria; portanto, a junção externa se tornou interna.
Caius Jard
sim, verifiquei que os resultados são os mesmos para: SELECT funds.id, prospects.id FROM perspectivas de fundsjunção interna em (prospects.id = funds.lead_id e prospects.is_manual = 'no') e SELECT funds.id, prospects.id DA fundsesquerda junte-se a prospects em (prospects.id = funds.lead_id) em que prospects.is_manual = 'não'
Rohit Dhiman
25

A maioria dos produtos RDBMS otimiza as duas consultas de forma idêntica. No "SQL Performance Tuning" de Peter Gulutzan e Trudy Pelzer, eles testaram várias marcas de RDBMS e não encontraram diferença de desempenho.

Prefiro manter as condições de junção separadas das condições de restrição de consulta.

Se você estiver usando OUTER JOINalgumas vezes, é necessário colocar condições na cláusula join.

Bill Karwin
fonte
1
Concordo com você que sintaticamente é mais limpo, e tenho que adiar o seu conhecimento desse livro e sua reputação muito alta, mas posso pensar em 4 consultas na última semana com planos de execução, tempos de CPU e leituras lógicas muito diferentes quando Mudei para onde os predicados para a associação.
22789 marr75
2
Você estava perguntando sobre as melhores práticas. Assim que você começa a testar como uma implementação específica do RDBMS funciona, outras pessoas deram o conselho correto: benchmark.
19411 Bill Karwin
12

ONDE será filtrado após a ocorrência de JOIN.

Filtre o JOIN para impedir a adição de linhas durante o processo JOIN.

TheTXI
fonte
10
Semanticamente, eles são impedidos durante o processo INNER JOIN, mas o otimizador pode reorganizar os predicados INNER JOIN e WHERE à vontade, para que o otimizador fique livre para excluí-los mais tarde, se desejar.
Cade Roux
1
Cade Roux: Certo. Muitas vezes, o que você escreve em SQL não é o que o otimizador fornecerá quando tudo estiver dito e feito. Eu poderia supor, então, que isso seria certo em um mundo todo-teoria, enquanto que a sua resposta é, naturalmente, mais correto no mundo dos otimizadores de consulta automáticas :)
TheTXI
Eu gosto desta explicação sobre a condição em:ON
Robert Rocha
3

Prefiro que o JOIN junte tabelas / exibições completas e, em seguida, use o WHERE Para apresentar o predicado do conjunto resultante.

Parece sintaticamente mais limpo.

Johnno Nolan
fonte
2

Normalmente, vejo o desempenho aumentar ao filtrar a junção. Especialmente se você puder ingressar em colunas indexadas para ambas as tabelas. Você também deve reduzir as leituras lógicas com a maioria das consultas, o que é, em um ambiente de alto volume, um indicador de desempenho muito melhor que o tempo de execução.

Sempre me divirto levemente quando alguém mostra seu benchmarking SQL e executa duas versões de um sproc 50.000 vezes à meia-noite no servidor de desenvolvimento e compara os tempos médios.

marr75
fonte
0

Colocar a condição na junção parece "semanticamente errada" para mim, pois não é para isso que servem os JOINs. Mas isso é muito qualitativo.

Problema adicional: se você decidir alternar de uma junção interna para, digamos, uma junção correta, ter a condição dentro de JOIN pode levar a resultados inesperados.

Jacob B
fonte
3
Às vezes, esses resultados são "esperados" e às vezes até "intencionais" (por exemplo, com junções externas, onde a condição WHERE tem semântica diferente da condição JOIN).
Marcel Toth
0

As junções são mais rápidas na minha opinião quando você tem uma tabela maior. Realmente não é muita diferença, especialmente se você estiver lidando com uma mesa menor. Quando soube pela primeira vez sobre junções, foi-me dito que as condições nas junções são exatamente como as condições da cláusula where e que eu poderia usá-las alternadamente se a cláusula where fosse específica sobre a tabela em que a condição seria executada.

Eric
fonte
-4

É melhor adicionar a condição na associação. O desempenho é mais importante que a legibilidade. Para conjuntos de dados grandes, isso importa.

Jeeno Shibu
fonte
1
Você tem algum tipo de prova, pesquisa como o posicionamento dos predicados mencionados afeta o desempenho?
Zso 01/12/19