Cláusula INNER JOIN ON vs WHERE

941

Para simplificar, suponha que todos os campos relevantes sejam NOT NULL.

Você pode fazer:

SELECT
    table1.this, table2.that, table2.somethingelse
FROM
    table1, table2
WHERE
    table1.foreignkey = table2.primarykey
    AND (some other conditions)

Se não:

SELECT
    table1.this, table2.that, table2.somethingelse
FROM
    table1 INNER JOIN table2
    ON table1.foreignkey = table2.primarykey
WHERE
    (some other conditions)

Esses dois funcionam da mesma maneira MySQL?

JCCyC
fonte
1
@Marco: aqui está #
Alexander Malakhov
1
possível duplicação da junção esquerda
Ciro Santilli
18
Se entendi corretamente, a primeira variante é a sintaxe implícita ANSI SQL-89 e a segunda variante é a sintaxe de junção explícita ANSI SQL-92. Ambos resultarão no mesmo resultado em conformidade com implementações SQL e ambos resultarão no mesmo plano de consulta em implementações SQL bem executadas. Pessoalmente, prefiro a sintaxe SQL-89, mas muitas pessoas preferem a sintaxe SQL-92.
Mikko Rantalainen
11
@Hogan Eu estava apontando os nomes oficiais para diferentes sintaxes. Nenhuma das respostas explicou explicitamente os nomes completos, então decidi adicioná-los como comentários. No entanto, meu comentário não respondeu à pergunta real, então eu a adicionei como comentário, não como resposta. (High votou respostas têm reivindicações como "INNER JOIN é sintaxe ANSI" e "implícito juntar sintaxe ANSI é mais velho", que não diz absolutamente nada, porque ambas as sintaxes diferentes sintaxes ANSI.)
Mikko Rantalainen

Respostas:

710

INNER JOIN é a sintaxe ANSI que você deve usar.

É geralmente considerado mais legível, especialmente quando você junta muitas tabelas.

Também pode ser facilmente substituído por um OUTER JOINsempre que necessário.

A WHEREsintaxe é mais orientada ao modelo relacional.

Um resultado de duas tabelas JOINed é um produto cartesiano das tabelas às quais um filtro é aplicado que seleciona apenas as linhas com as colunas de junção correspondentes.

É mais fácil ver isso com a WHEREsintaxe.

Como no seu exemplo, no MySQL (e no SQL geralmente) essas duas consultas são sinônimos.

Observe também que o MySQL também possui uma STRAIGHT_JOINcláusula.

Usando esta cláusula, você pode controlar a JOINordem: qual tabela é varrida no loop externo e qual está no loop interno.

Você não pode controlar isso no MySQL usando WHEREsintaxe.

Quassnoi
fonte
10
Obrigado, Quassnoi. Você tem muitos detalhes em suas ans; é justo dizer que "sim, essas consultas são equivalentes, mas você deve usar a junção interna porque é mais legível e mais fácil de modificar"?
Allyourcode
8
@allyourcode: para Oracle, SQL Server, MySQLe PostgreSQL- sim. Para outros sistemas, provavelmente também, mas é melhor verificar.
Quassnoi 26/07/09
13
FWIW, usar vírgulas com condições de junção na WHEREcláusula também está no padrão ANSI.
Bill Karwin
1
@Bill Karwin: JOINkeyword não fazia parte dos padrões proprietários até o passado mais recente que possa parecer. Ele entrou Oracleapenas na versão 9e PostgreSQLna versão 7.2(ambos lançados em 2001). A aparência dessa palavra-chave fazia parte da ANSIadoção padrão, e é por isso que essa palavra-chave geralmente está associada ANSI, apesar do fato de que a última também suporta vírgula como sinônimo CROSS JOIN.
Quassnoi 13/01/10
9
No entanto, as junções especificadas pelo ANSI SQL-89 devem ser feitas com vírgulas e condições em uma WHEREcláusula (sem condições, uma junção é equivalente a uma junção cruzada, como você disse). O ANSI SQL-92 adicionou a JOINpalavra - chave e a sintaxe relacionada, mas a sintaxe no estilo de vírgula ainda é suportada para compatibilidade com versões anteriores.
Bill Karwin
182

Outros apontaram que INNER JOIN ajuda a legibilidade humana, e essa é uma prioridade, eu concordo.
Deixe-me tentar explicar por que a sintaxe de junção é mais legível.

Uma SELECTconsulta básica é esta:

SELECT stuff
FROM tables
WHERE conditions

A SELECTcláusula nos diz o que estamos voltando; a FROMcláusula nos diz de onde estamos obtendo e oWHERE cláusula nos diz quais estamos obtendo.

JOIN é uma declaração sobre as tabelas, como elas são unidas (conceitualmente, na verdade, em uma única tabela).

Quaisquer elementos de consulta que controlam as tabelas - de onde estamos obtendo coisas - pertencem semanticamente à FROMcláusula (e, é claro, é para onde os JOINelementos vão). Colocar elementos de junção na WHEREcláusula conflita com o que e de onde , é por isso que a JOINsintaxe é preferida.

Carl Manaster
fonte
7
Obrigado por esclarecer por que a união interna é preferida, Carl. Eu acho que seu ans estava implícito nos outros, mas explícito geralmente é melhor (sim, eu sou um fã de Python).
Allyourcode
2
A semântica de ON e WHERE significa que, para JOINs após a última OUTER JOIN , não importa qual você use. Embora você caracterize ON como parte do JOIN, também é uma filtragem após um produto cartesiano. Tanto ON quanto ON filtram um produto cartesiano. Mas ON ou uma sub-seleção com WHERE deve ser usada antes da última OUTER JOIN. (JOINs não são "em" pares de colunas Quaisquer duas tabelas podem ser agrupadas em qualquer condição Isso é apenas uma maneira de interpretar associações em igualdade de colunas especificamente...)
philipxy
Mesmo quando você estiver usando WHERE com o mesmo efeito de INNER JOIN, mencionará suas duas tabelas na parte FROM da consulta. Então, basicamente, você ainda está implicando onde você está recebendo seus dados na cláusula FROM, então eu acho que você não pode dizer que necessariamente "confunde o que eo que-from"
cybergeek654
@ArsenKhachaturyan Só porque uma palavra-chave ou identificador é usada no texto não significa que ele é código e precisa de formato de código. Essa é uma opção de formatação que pode ser usada de qualquer maneira; se é razoável editar aqui, é justificável que cada post seja constantemente editado para outro formato - ou seja, não é justificável. (Além disso, pode ser difícil ler o formato de código embutido por palavra.) O mesmo para as quebras de parágrafos aqui - elas não são particularmente claras. Mesmo com 'qual' vs 'que'. E os nomes das linguagens de programação não devem estar no formato de código. PS Você adicionou uma quebra de linha por erro.
philipxy 19/04
@ philipxy como você mencionou "isso não significa ...", mas obviamente nem isso significava que não pode ser marcado com a palavra-chave code. Sim, é uma escolha a ser feita, mas muitas postagens são feitas sem o conhecimento desse fato. Portanto, minha decisão de fazer as alterações não tem como objetivo quebrar nada, mas torná-lo mais legível. Se você notou alguma interrupção após a formatação das alterações, desculpe-se por isso e obviamente pode reverter essas alterações.
Arsen Khachaturyan
143

Aplicando Instruções Condicionais em ON / WHERE

Aqui eu expliquei sobre as etapas do processamento de consultas lógicas.


Referência: Por dentro do Microsoft® SQL Server ™ 2005 T-SQL Querying
Editor: Microsoft Press
Pub Data: 07 de março de 2006
Imprimir ISBN-10: 0-7356-2313-9
Imprimir ISBN-13: 978-0-7356-2313-2
Páginas: 640

Por dentro do Microsoft® SQL Server ™ 2005 Consulta T-SQL

(8)  SELECT (9) DISTINCT (11) TOP <top_specification> <select_list>
(1)  FROM <left_table>
(3)       <join_type> JOIN <right_table>
(2)       ON <join_condition>
(4)  WHERE <where_condition>
(5)  GROUP BY <group_by_list>
(6)  WITH {CUBE | ROLLUP}
(7)  HAVING <having_condition>
(10) ORDER BY <order_by_list>

O primeiro aspecto perceptível do SQL que é diferente de outras linguagens de programação é a ordem na qual o código é processado. Na maioria das linguagens de programação, o código é processado na ordem em que está escrito. No SQL, a primeira cláusula processada é a cláusula FROM, enquanto a cláusula SELECT, que aparece primeiro, é processada quase por último.

Cada etapa gera uma tabela virtual usada como entrada para a etapa a seguir. Essas tabelas virtuais não estão disponíveis para o chamador (aplicativo cliente ou consulta externa). Somente a tabela gerada pela etapa final é retornada ao chamador. Se uma determinada cláusula não for especificada em uma consulta, a etapa correspondente será simplesmente ignorada.

Breve descrição das fases de processamento de consultas lógicas

Não se preocupe muito se a descrição das etapas não parece fazer muito sentido por enquanto. Estes são fornecidos como referência. As seções que vêm após o exemplo do cenário abordarão as etapas com muito mais detalhes.

  1. FROM: um produto cartesiano (junção cruzada) é executado entre as duas primeiras tabelas na cláusula FROM e, como resultado, a tabela virtual VT1 é gerada.

  2. LIGADO: O filtro LIGADO é aplicado ao VT1. Somente linhas para as quais <join_condition>é TRUE são inseridas no VT2.

  3. OUTER (junção): se um OUTER JOIN for especificado (em oposição a CROSS JOIN ou INNER JOIN), as linhas da tabela preservada ou as tabelas para as quais não foi encontrada uma correspondência serão adicionadas às linhas do VT2 como linhas externas, gerando VT3. Se mais de duas tabelas aparecerem na cláusula FROM, as etapas 1 a 3 serão aplicadas repetidamente entre o resultado da última associação e a próxima tabela na cláusula FROM até que todas as tabelas sejam processadas.

  4. ONDE: O filtro ONDE é aplicado ao VT3. Somente linhas para as quais <where_condition>é TRUE são inseridas no VT4.

  5. GROUP BY: As linhas do VT4 são organizadas em grupos com base na lista de colunas especificada na cláusula GROUP BY. VT5 é gerado.

  6. CUBO ROLLUP: Supergrupos (grupos de grupos) são adicionados às linhas do VT5, gerando VT6.

  7. HAVING: O filtro HAVING é aplicado ao VT6. Somente grupos para os quais <having_condition>é TRUE são inseridos no VT7.

  8. SELECT: A lista SELECT é processada, gerando VT8.

  9. DISTINCT: Linhas duplicadas são removidas do VT8. VT9 é gerado.

  10. ORDER BY: As linhas do VT9 são classificadas de acordo com a lista de colunas especificada na cláusula ORDER BY. Um cursor é gerado (VC10).

  11. TOPO: O número ou porcentagem de linhas especificado é selecionado desde o início do VC10. A tabela VT11 é gerada e retornada ao chamador.



Portanto, (INNER JOIN) ON filtrará os dados (a contagem de dados da VT será reduzida aqui) antes de aplicar a cláusula WHERE. As condições de junção subsequentes serão executadas com dados filtrados, o que melhora o desempenho. Depois disso, apenas a condição WHERE aplicará as condições de filtro.

(A aplicação de instruções condicionais em ON / WHERE não fará muita diferença em alguns casos. Isso depende de quantas tabelas você ingressou e do número de linhas disponíveis em cada tabela de ingresso)

rafidheen
fonte
10
"Portanto, (INNER JOIN) ON filtrará os dados (a contagem de dados do VT será reduzida aqui) antes de aplicar a cláusula WHERE." Não necessariamente. O artigo é sobre a ordem lógica do processamento. Quando você diz que uma implementação específica fará uma coisa antes de outra, você está falando sobre a ordem de processamento implementada . As implementações podem fazer as otimizações que quiserem, desde que o resultado seja o mesmo que se a implementação seguisse a ordem lógica. Joe Celko escreveu muito sobre isso na Usenet.
Mike Sherrill 'Cat Recall'
@rafidheen "(INNER JOIN) ON filtrará os dados ... antes de aplicar a cláusula WHERE ... que melhora o desempenho." Bom ponto. "Depois disso, apenas a condição WHERE aplicará as condições de filtro". E a cláusula HAVING?
James
@ James Essa afirmação de rafidheen está errada. Consulte 'otimização de junção' no manual. Também meus outros comentários nesta página. (E MikeSherrill'CatRecall '.) Essas descrições "lógicas" descrevem o valor do resultado, não como ele é realmente calculado. E esse comportamento de implementação não garante que não mude.
philipxy
67

A sintaxe implícita da junção ANSI é mais antiga, menos óbvia e não recomendada.

Além disso, a álgebra relacional permite a intercambiabilidade dos predicados na WHEREcláusula e INNER JOIN, portanto, mesmo INNER JOINconsultas comWHERE cláusulas podem ter os predicados reorganizados pelo otimizador.

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

Às vezes, isso inclui tornar o INNER JOIN"incompleto" relativamente 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

Escreva:

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
16
Seu primeiro trecho definitivamente machuca mais meu cérebro. Alguém realmente faz isso? Se eu encontrar alguém que faça isso, está tudo bem em eu bater na cabeça dele?
Allyourcode
3
Eu localizo os critérios onde faz mais sentido. Se eu estiver ingressando em uma tabela de pesquisa de instantâneo consistente temporalmente (e não tiver uma exibição ou UDF que imponha a seleção de uma data válida), incluirei a data efetiva na associação e não no WHERE porque é menos provavelmente removidos acidentalmente.
Cade Roux
14
@allyourcode: embora seja raro ver esse tipo de sintaxe de junção em INNER JOINs, é bastante comum para RIGHT JOINs e LEFT JOINS - especificar mais detalhes no predicado de junção elimina a necessidade de uma subconsulta e impede que suas junções externas sejam inadvertidamente ativadas em INNER JOINs. (Embora eu concordo que para associações internas eu quase sempre colocar c.State = 'NY' na cláusula WHERE)
Dave Markle
1
@allyourcode Eu definitivamente faço isso! E eu concordo com Cade .. Estou curioso para saber se há uma razão decente para não
Arth
31

Junções implícitas (que é conhecida como sua primeira consulta) se tornam muito mais confusas, difíceis de ler e difíceis de manter quando você precisa começar a adicionar mais tabelas à sua consulta. Imagine fazer a mesma consulta e tipo de junção em quatro ou cinco tabelas diferentes ... é um pesadelo.

O uso de uma associação explícita (seu segundo exemplo) é muito mais legível e fácil de manter.

matt b
fonte
48
Eu não poderia discordar mais. A sintaxe JOIN é extremamente prolixo e difícil de organizar. Eu tenho muitas consultas juntando 5, 10 e até 15 tabelas usando a cláusula WHERE joins e elas são perfeitamente legíveis. Reescrever essa consulta usando uma sintaxe JOIN resulta em uma bagunça ilegível. O que só mostra que não há resposta certa para essa pergunta e que depende mais do que você se sente confortável.
Noah Yetter
33
Noah, acho que você pode estar em minoria aqui.
Matt b
2
Recebo +1 em Matt e Noah. Eu gosto de diversidade :). Eu posso ver de onde Noé está vindo; A junção interna não adiciona nada de novo ao idioma e é definitivamente mais detalhada. Por outro lado, pode tornar sua condição de 'onde' muito mais curta, o que geralmente significa que é mais fácil de ler.
Allyourcode
5
Eu assumiria que qualquer SGBD sadio converteria as duas consultas no mesmo plano de execução; no entanto, na realidade, cada DBMS é diferente e a única maneira de saber com certeza é realmente examinar o plano de execução (ou seja, você mesmo deverá testá-lo).
matt b
É verdade que @rafidheen sugeriu em outra resposta (aquela com a sequência detalhada da execução do SQL) que os JOINs são filtrados um de cada vez, reduzindo o tamanho das operações de junção quando comparadas a uma junção cartesiana completa de 3 ou mais tabelas, com o filtro WHERE sendo aplicado retroativamente? Nesse caso, sugeriria que JOIN oferece melhoria de desempenho (além de vantagens nas junções esquerda / direita, como também apontado em outra resposta).
James
26

Também apontarei que o uso da sintaxe mais antiga está mais sujeito a erros. Se você usar junções internas sem uma cláusula ON, você receberá um erro de sintaxe. Se você usar a sintaxe mais antiga e esquecer uma das condições de junção na cláusula where, receberá uma junção cruzada. Os desenvolvedores geralmente corrigem isso adicionando a palavra-chave distinta (em vez de corrigir a junção porque ainda não percebem que a junção está quebrada), que pode parecer curar o problema, mas diminuirá consideravelmente a consulta.

Além disso, para manutenção, se você tiver uma junção cruzada na sintaxe antiga, como o mantenedor saberá se você pretende ter uma (há situações em que são necessárias junções cruzadas) ou se foi um acidente que deve ser corrigido?

Deixe-me apontar para esta pergunta para ver por que a sintaxe implícita é ruim se você usa junções esquerdas. Sybase * = para Ansi Standard com 2 tabelas externas diferentes para a mesma tabela interna

Além disso (discurso pessoal aqui), o padrão que utiliza as junções explícitas tem mais de 20 anos, o que significa que a sintaxe implícita da junção está desatualizada nesses 20 anos. Você escreveria código de aplicativo usando sintaxe desatualizada por 20 anos? Por que você deseja escrever o código do banco de dados?

HLGEM
fonte
3
@HLGEM: Embora eu concorde completamente que JOINs explícitos são melhores, há casos em que você só precisa usar a sintaxe antiga. Um exemplo do mundo real: o ANSI JOIN entrou no Oracle apenas na versão 9i, lançada em 2001, e até apenas um ano atrás (16 anos desde o momento em que o padrão foi publicado) eu tive que suportar várias instalações do 8i para as quais tínhamos para liberar atualizações críticas. Como não queria manter dois conjuntos de atualizações, desenvolvemos e testamos as atualizações em todos os bancos de dados, incluindo o 8i, o que significava que não era possível usar ANSI JOINs.
Quassnoi
+1 ponto interessante quando você ressalta que a sintaxe sem INNER JOIN é mais suscetível a erros. Estou confuso com a sua última frase quando você diz "... o padrão usando as junções explícitas tem 17 anos". então você está sugerindo usar a palavra-chave INNER JOIN ou não?
Marco Demaio
1
@ Marco Demaio, sim, sempre use INNER JOIN ou JOIN (esses dois são os mesmos) ou LEFT JOIN ou RIGHT JOIN ou CROSS JOIN e nunca use as junções de vírgula implícitas.
HLGEM
2
"Por que você deseja escrever um código de banco de dados com 20 anos?" - Percebo que você escreve SQL usando HAVING'desatualizado' desde que o SQL começou a suportar tabelas derivadas. Percebo também que você não usa, NATURAL JOINmesmo que eu argumentasse que ficou INNER JOIN"desatualizado". Sim, você tem suas razões (não há necessidade de declará-las novamente aqui!): Meu argumento é que aqueles que gostam de usar a sintaxe mais antiga também têm suas razões e a idade relativa da sintaxe é de pouca ou nenhuma relevância.
onedaywhen
1
ONDE ainda está no padrão (mostre-me onde não está). Então, nada desatualizado, aparentemente. Além disso, "em vez de corrigir a junção" mostra-me um desenvolvedor que deve ser mantido longe dos DBMSs em geral, longe .
Jürgen A. Erhard,
12

Eles têm um significado legível para humanos.

No entanto, dependendo do otimizador de consulta, eles podem ter o mesmo significado para a máquina.

Você deve sempre codificar para ser legível.

Ou seja, se esse é um relacionamento interno, use a associação explícita. se você estiver correspondendo a dados pouco relacionados, use a cláusula where.

John Gietzen
fonte
11

O padrão SQL: 2003 alterou algumas regras de precedência para que uma instrução JOIN tenha precedência sobre uma junção "vírgula". Na verdade, isso pode alterar os resultados da sua consulta, dependendo de como está configurada. Isso causa alguns problemas para algumas pessoas quando o MySQL 5.0.12 mudou para aderir ao padrão.

Portanto, no seu exemplo, suas consultas funcionariam da mesma forma. Mas se você adicionou uma terceira tabela: SELECT ... FROM tabela1, tabela2 JOIN table3 ON ... WHERE ...

Antes do MySQL 5.0.12, a tabela1 e a tabela2 seriam unidas primeiro, depois a tabela3. Agora (5.0.12 e diante), tabela2 e tabela3 são unidas primeiro, depois tabela1. Nem sempre muda os resultados, mas pode e você pode nem perceber.

Eu nunca mais uso a sintaxe "vírgula", optando pelo seu segundo exemplo. De qualquer forma, é muito mais legível, as condições JOIN estão com os JOINs, não separadas em uma seção de consulta separada.

Brent Baisley
fonte
SQL padrão não mudou. O MySQL estava errado e agora está certo. Veja o manual do MySQL.
usar o seguinte código
4

Eu sei que você está falando sobre o MySQL, mas de qualquer maneira: no Oracle 9 junções explícitas e junções implícitas gerariam planos de execução diferentes. AFAIK que foi resolvido no Oracle 10+: não existe mais essa diferença.

João Marcus
fonte
1

A sintaxe de junção ANSI é definitivamente mais portátil.

Estou passando por uma atualização do Microsoft SQL Server e também mencionaria que a sintaxe = * e * = para associações externas no SQL Server não é suportada (sem modo de compatibilidade) para o servidor sql de 2005 e versões posteriores.

Benzo
fonte
2
Mesmo no SQL Server 2000, = e = podem dar resultados errados e nunca devem ser usados.
21119 HLGEM
2
*=e =*nunca foram ANSI e nunca foram uma boa notação. É por isso que ON foi necessário - para as junções externas na ausência de subselects (que foi adicionado ao mesmo tempo, para que eles não são realmente necessários em CRUZ & associações internas.)
philipxy
1

Se você costuma programar procedimentos armazenados dinâmicos, se apaixona pelo seu segundo exemplo (usando where). Se você tiver vários parâmetros de entrada e muita confusão de metamorfose, essa é a única maneira. Caso contrário, ambos executarão o mesmo plano de consulta, portanto, definitivamente não haverá diferença óbvia nas consultas clássicas.

Kviz Majster
fonte