SQL deixou junção vs várias tabelas na linha FROM?

256

A maioria dos dialetos SQL aceita as seguintes consultas:

SELECT a.foo, b.foo
FROM a, b
WHERE a.x = b.x

SELECT a.foo, b.foo
FROM a
LEFT JOIN b ON a.x = b.x

Agora, obviamente, quando você precisa de uma associação externa, a segunda sintaxe é necessária. Mas, ao fazer uma junção interna, por que devo preferir a segunda sintaxe à primeira (ou vice-versa)?

jmucchiello
fonte
1
Guffa: Como você achou isso? Embora a minha pergunta é a melhor prática mais do que "como eu"
jmucchiello
Como é uma prática recomendada, faça deste um Wiki.
23770 Binoj Antony
1
Eu acho que ninguém comentou sobre o desempenho desses dois. Alguém pode confirmar ou citar algo razoável em relação a diferenças significativas?
ahnbizcad
@ahnbizcad As duas consultas fornecidas não fazem a mesma coisa. O primeiro retorna o mesmo que um INNER JOIN ON. A implementação é específica da versão do DBMS e, mesmo assim, tem poucas garantias. Porém, as transformações de DBMS que equivalem a casos de vírgula vs INNER JOIN ON / WHERE vs CROSS JOIN WHERE são triviais. Aprenda sobre otimização / implementação de consulta de banco de dados relacional.
Philipxy
recebeu uma recomendação de recursos? manuais gigantescos e densos são o motivo de eu tentar aprender daqui.
Ahnbizcad 13/05/2019

Respostas:

319

A sintaxe antiga, com apenas a listagem das tabelas e o uso da WHEREcláusula para especificar os critérios de junção, está sendo preterida na maioria dos bancos de dados modernos.

Não é apenas para mostrar, a sintaxe antiga tem a possibilidade de ser ambígua quando você usa junções INNER e OUTER na mesma consulta.

Deixe-me lhe dar um exemplo.

Vamos supor que você tenha 3 tabelas no seu sistema:

Company
Department
Employee

Cada tabela contém várias linhas, vinculadas. Você tem várias empresas e cada empresa pode ter vários departamentos e cada departamento pode ter vários funcionários.

Ok, agora você quer fazer o seguinte:

Liste todas as empresas e inclua todos os seus departamentos e todos os seus funcionários. Observe que algumas empresas ainda não têm departamentos, mas inclua-os também. Recupere apenas os departamentos que possuem funcionários, mas sempre liste todas as empresas.

Então você faz isso:

SELECT * -- for simplicity
FROM Company, Department, Employee
WHERE Company.ID *= Department.CompanyID
  AND Department.ID = Employee.DepartmentID

Observe que o último existe uma junção interna, para atender aos critérios de que você deseja apenas departamentos com pessoas.

Ok, então o que acontece agora. Bem, o problema é que depende do mecanismo do banco de dados, do otimizador de consulta, índices e estatísticas da tabela. Deixe-me explicar.

Se o otimizador de consultas determinar que a maneira de fazer isso é primeiro contratar uma empresa, encontrar os departamentos e fazer uma junção interna com os funcionários, você não terá empresas que não tenham departamentos.

A razão para isso é que a WHEREcláusula determina quais linhas terminam no resultado final, não partes individuais das linhas.

E, nesse caso, devido à junção esquerda, a coluna Department.ID será NULL e, portanto, quando se trata de INNER JOIN to Employee, não há como atender a essa restrição para a linha Employee e, portanto, não aparecer.

Por outro lado, se o otimizador de consultas decidir atacar a junção departamento-funcionário primeiro e depois fazer uma junção esquerda com as empresas, você as verá.

Portanto, a sintaxe antiga é ambígua. Não há como especificar o que você deseja, sem lidar com dicas de consulta, e alguns bancos de dados não têm como.

Digite a nova sintaxe, com isso você pode escolher.

Por exemplo, se você deseja todas as empresas, como a descrição do problema afirmou, é isso que você escreveria:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID

Aqui você especifica que deseja que a junção departamento-funcionário seja feita como uma junção e, em seguida, deixa a junção dos resultados disso com as empresas.

Além disso, digamos que você queira apenas departamentos que contenham a letra X em seus nomes. Novamente, com as junções de estilo antigo, você corre o risco de perder a empresa também, se ela não tiver departamentos com um X no nome, mas com a nova sintaxe, você poderá fazer o seguinte:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID AND Department.Name LIKE '%X%'

Essa cláusula extra é usada para a união, mas não é um filtro para a linha inteira. Portanto, a linha pode aparecer com informações da empresa, mas pode ter NULLs em todas as colunas de departamento e funcionário dessa linha, porque não há departamento com um X no nome dessa empresa. Isso é difícil com a sintaxe antiga.

É por isso que, entre outros fornecedores, a Microsoft descontinuou a sintaxe de junção externa antiga, mas não a sintaxe de junção interna antiga, desde o SQL Server 2005 e superior. A única maneira de conversar com um banco de dados em execução no Microsoft SQL Server 2005 ou 2008, usando a sintaxe de junção externa do estilo antigo, é defini-lo no modo de compatibilidade 8.0 (também conhecido como SQL Server 2000).

Além disso, da maneira antiga, jogando várias tabelas no otimizador de consultas, com várias cláusulas WHERE, era semelhante a dizer "aqui está você, faça o melhor que puder". Com a nova sintaxe, o otimizador de consulta tem menos trabalho a fazer para descobrir quais partes estão juntas.

Então aí está.

ESQUERDA e INTERIOR JOIN é a onda do futuro.

Lasse V. Karlsen
fonte
28
"está sendo preterido na maioria dos bancos de dados modernos." --- apenas curioso, quais?
Zerkms 12/04
10
perdoe-me, não estou familiarizado com o operador * =, o que ele faz? obrigado!
ultrajohn
9
Estrela = e = Estrela são (bem eram) as junções externas direita e esquerda, ou é esquerda e direita? Foi preterido por muito tempo, eu não tê-los usado desde SQL Server 6.
Tony Hopkinson
3
A vírgula não está obsoleta. A OUTER JOINsintaxe nunca padrão *=/ =*/ *=*está obsoleta.
philipxy
1
Essa resposta nem mesmo responde à pergunta, que não é sobre junções externas. A afirmação que ele faz sobre vírgula vs INNER JOIN ON, re otimização, está errada.
Philipxy
17

A sintaxe JOIN mantém as condições próximas à tabela à qual se aplicam. Isso é especialmente útil quando você ingressa em uma grande quantidade de tabelas.

A propósito, você também pode fazer uma junção externa com a primeira sintaxe:

WHERE a.x = b.x(+)

Ou

WHERE a.x *= b.x

Ou

WHERE a.x = b.x or a.x not in (select x from b)
Andomar
fonte
2
A sintaxe * = foi descontinuada no MS SQLServer e por um bom motivo: não apenas torna mais difícil a leitura, mas também não faz o que as pessoas pensam que faz e NÃO é o mesmo que um LEFT JOIN com aparência semelhante. A sintaxe (+) não é familiar para mim; que implementação SQL faz isso?
Euro Micelli 21/05/2009
2
A outra sintaxe é usada pelo Oracle, pelo menos.
Lasse V. Karlsen
4
Nunca use a sintaxe do SQL Server * =, NÃO fornecerá resultados consistentes, pois às vezes interpretará como uma junção cruzada e não uma esquerda. Isso acontece mesmo no SQL Server 2000. Se você tiver algum código usando isso, precisará corrigir.
HLGEM
12

A primeira maneira é o padrão mais antigo. O segundo método foi introduzido no SQL-92, http://en.wikipedia.org/wiki/SQL . O padrão completo pode ser visto em http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt .

Levou muitos anos para as empresas de banco de dados adotarem o padrão SQL-92.

Portanto, a razão pela qual o segundo método é preferido, é o padrão SQL de acordo com o comitê de padrões ANSI e ISO.

Dwight T
fonte
,ainda é padrão. onprecisavam ser introduzidos apenas outer joinuma vez que as sub-seleções também foram introduzidas.
precisa
12

Basicamente, quando sua cláusula FROM lista tabelas da seguinte maneira:

SELECT * FROM
  tableA, tableB, tableC

o resultado é um produto cruzado de todas as linhas nas tabelas A, B, C. Em seguida, você aplica a restrição WHERE tableA.id = tableB.a_idque descartará um grande número de linhas e depois ... AND tableB.id = tableC.b_ide você deve obter apenas as linhas que realmente lhe interessam no.

Os DBMSs sabem como otimizar esse SQL para que a diferença de desempenho ao escrever isso usando JOINs seja insignificante (se houver). O uso da notação JOIN torna a instrução SQL mais legível (IMHO, não usar junções transforma a instrução em uma bagunça). Usando o produto cruzado, é necessário fornecer critérios de junção na cláusula WHERE, e esse é o problema com a notação. Você está lotando sua cláusula WHERE com coisas como

    tableA.id = tableB.a_id 
AND tableB.id = tableC.b_id 

que é usado apenas para restringir o produto cruzado. A cláusula WHERE deve conter apenas RESTRIÇÕES ao conjunto de resultados. Se você combinar critérios de junção de tabela com restrições do conjunto de resultados, você (e outros) achará sua consulta mais difícil de ler. Você definitivamente deve usar JOINs e manter a cláusula FROM uma cláusula FROM e a cláusula WHERE uma cláusula WHERE.

Peter Perháč
fonte
10

O segundo é preferido porque é muito menos provável que resulte em uma junção cruzada acidental, esquecendo de colocar na cláusula where. Uma junção sem cláusula on falhará na verificação de sintaxe, uma junção de estilo antigo sem cláusula where não falhará, fará uma junção cruzada.

Além disso, quando mais tarde você precisar fazer uma junção esquerda, é útil para manutenção que todos estejam na mesma estrutura. E a sintaxe antiga está desatualizada desde 1992, é hora de parar de usá-la.

Além disso, descobri que muitas pessoas que usam exclusivamente a primeira sintaxe não entendem realmente as junções e a compreensão das junções é essencial para obter resultados corretos ao consultar.

HLGEM
fonte
6

Eu acho que existem algumas boas razões nesta página para adotar o segundo método - usando JOINs explícitos. O argumento decisivo é que, quando os critérios JOIN são removidos da cláusula WHERE, fica muito mais fácil ver os demais critérios de seleção na cláusula WHERE.

Em instruções SELECT realmente complexas, fica muito mais fácil para o leitor entender o que está acontecendo.

Alan G
fonte
5

A SELECT * FROM table1, table2, ...sintaxe está correta para algumas tabelas, mas se torna exponencialmente ( não necessariamente uma declaração matematicamente precisa) ) cada vez mais difícil de ler à medida que o número de tabelas aumenta.

A sintaxe JOIN é mais difícil de escrever (no início), mas torna explícito quais critérios afetam quais tabelas. Isso torna muito mais difícil cometer um erro.

Além disso, se todas as junções forem INNER, as duas versões serão equivalentes. No entanto, no momento em que você tem uma junção EXTERNA em qualquer lugar da declaração, as coisas ficam muito mais complicadas e é praticamente garantido que o que você escreve não estará consultando o que acha que escreveu.

Euro Micelli
fonte
2

Quando você precisa de uma junção externa, a segunda sintaxe não é sempre é necessária:

Oráculo:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x = b.x(+)

MSSQLServer (embora tenha sido descontinuado na versão 2000) / Sybase:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x *= b.x

Mas voltando à sua pergunta. Não sei a resposta, mas provavelmente está relacionada ao fato de uma associação ser mais natural (pelo menos sintaticamente) do que adicionar uma expressão a uma cláusula where quando você está fazendo exatamente isso: união .

Pablo Santa Cruz
fonte
O SQL Server descontinuou a sintaxe de junção esquerda e, mesmo no SQL Server 2000, ele não fornece resultados corretos de maneira consistente (às vezes realiza uma junção cruzada em vez de uma junção esquerda) e nunca deve ser usado no SQL Server.
HLGEM
@HLGEM: Obrigado pela informação. Vou atualizar meu post para refletir o que você está dizendo.
Pablo Santa Cruz
0

Eu ouço muitas pessoas reclamando que o primeiro é muito difícil de entender e que não está claro. Não vejo problema com isso, mas depois de ter essa discussão, uso a segunda até no INNER JOINS para maior clareza.

kemiller2002
fonte
1
Fui criado com o hábito de não usar a sintaxe JOIN e fazê-lo da primeira maneira. Devo admitir que eu ainda estou preso no hábito muitas vezes só porque eu acho que meu cérebro foi condicionado a seguir essa lógica, wheras a sintaxe juntar-se às vezes para me parece difícil pensar no.
TheTXI
3
Eu também fui ensinado dessa maneira. Mudei meu estilo de codificação, porque as pessoas olhavam para ele e não reconheciam facilmente o que estava acontecendo. Como não há diferença lógica e não encontro razão para escolher o primeiro em detrimento do último, achei que deveria me adaptar para tornar o código mais claro para ajudar os outros a entender o que eu escrevo.
Kemiller2002
0

Para o banco de dados, eles acabam sendo os mesmos. Para você, porém, você terá que usar essa segunda sintaxe em algumas situações. Por questões de edição de consultas que acabam tendo que usá-lo (descobrindo que você precisava de uma junção esquerda onde você tinha uma junção direta) e, por questões de consistência, eu usaria apenas o segundo método. Isso tornará as consultas de leitura mais fáceis.

Jeff Ferland
fonte
0

Bem, a primeira e a segunda consultas podem gerar resultados diferentes porque um LEFT JOIN inclui todos os registros da primeira tabela, mesmo se não houver registros correspondentes na tabela correta.

Gavin H
fonte