Jointure gauche SQL vs plusieurs tables sur la ligne FROM?

256

La plupart des dialectes SQL acceptent les deux requêtes suivantes:

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

Maintenant, évidemment, lorsque vous avez besoin d'une jointure externe, la deuxième syntaxe est requise. Mais lorsque je fais une jointure interne, pourquoi devrais-je préférer la deuxième syntaxe à la première (ou vice versa)?

jmucchiello
la source
1
Guffa: Comment avez-vous trouvé cela? Bien que ma question soit plus une bonne pratique que "comment faire"
jmucchiello
Comme c'est la meilleure pratique, veuillez en faire un Wiki.
Binoj Antony
1
Je pense que personne n'a commenté la performance de ces deux-là. Quelqu'un peut-il confirmer ou citer quelque chose de raisonnable concernant des différences importantes?
ahnbizcad
@ahnbizcad Les deux requêtes données ne font pas la même chose. La première renvoie la même chose qu'un INNER JOIN ON. L'implémentation est spécifique à la version du SGBD, et même alors, elle a peu de garanties. Mais les transformations de SGBD équivalant aux cas de virgule vs INNER JOIN ON / WHERE vs CROSS JOIN WHERE est triviale. En savoir plus sur l'optimisation / l'implémentation de requêtes de bases de données relationnelles.
philipxy
obtenu une recommandation de ressources? des manuels gigantesques et denses sont la raison pour laquelle j'essaie d'apprendre d'ici.
ahnbizcad

Réponses:

319

L'ancienne syntaxe, avec juste la liste des tables et l'utilisation de la WHEREclause pour spécifier les critères de jointure, est obsolète dans la plupart des bases de données modernes.

Ce n'est pas seulement pour le show, l'ancienne syntaxe a la possibilité d'être ambiguë lorsque vous utilisez les jointures INNER et OUTER dans la même requête.

Laisse moi te donner un exemple.

Supposons que vous ayez 3 tables dans votre système:

Company
Department
Employee

Chaque table contient de nombreuses lignes, liées entre elles. Vous avez plusieurs entreprises, et chaque entreprise peut avoir plusieurs départements, et chaque département peut avoir plusieurs employés.

Ok, alors maintenant vous voulez faire ce qui suit:

Répertoriez toutes les entreprises et incluez tous leurs services et tous leurs employés. Notez que certaines entreprises n'ont pas encore de service, mais assurez-vous de les inclure également. Assurez-vous de ne récupérer que les départements qui ont des employés, mais répertoriez toujours toutes les entreprises.

Vous faites donc ceci:

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

Notez que le dernier il y a une jointure interne, afin de remplir les critères que vous ne voulez que des départements avec des personnes.

Ok, alors qu'est-ce qui se passe maintenant. Eh bien, le problème est que cela dépend du moteur de base de données, de l'optimiseur de requêtes, des index et des statistiques de table. Laisse-moi expliquer.

Si l'optimiseur de requêtes détermine que la façon de procéder consiste à prendre d'abord une entreprise, puis à trouver les services, puis à effectuer une jointure interne avec les employés, vous n'obtiendrez aucune entreprise qui n'a pas de service.

La raison en est que la WHEREclause détermine quelles lignes se retrouvent dans le résultat final, et non des parties individuelles des lignes.

Et dans ce cas, en raison de la jointure gauche, la colonne Department.ID sera NULL, et donc en ce qui concerne la jointure interne à l'employé, il n'y a aucun moyen de respecter cette contrainte pour la ligne Employé, et donc ce ne sera pas le cas. apparaître.

En revanche, si l'optimiseur de requêtes décide d'aborder la jointure département-employé, puis de faire une jointure gauche avec les entreprises, vous les verrez.

L'ancienne syntaxe est donc ambiguë. Il n'y a aucun moyen de spécifier ce que vous voulez, sans traiter des indices de requête, et certaines bases de données n'ont aucun moyen.

Entrez la nouvelle syntaxe, avec celle que vous pouvez choisir.

Par exemple, si vous voulez toutes les entreprises, comme l'indique la description du problème, voici ce que vous écririez:

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

Ici, vous spécifiez que vous souhaitez que la jointure département-employé se fasse en une seule jointure, puis vous laissez les résultats de cette jointure avec les entreprises.

De plus, supposons que vous ne souhaitiez que les départements contenant la lettre X dans leur nom. Encore une fois, avec les jointures à l'ancienne, vous risquez également de perdre la société, si elle n'a pas de départements avec un X dans son nom, mais avec la nouvelle syntaxe, vous pouvez le faire:

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

Cette clause supplémentaire est utilisée pour la jointure, mais n'est pas un filtre pour la ligne entière. Ainsi, la ligne peut apparaître avec des informations sur la société, mais peut avoir des valeurs NULL dans toutes les colonnes département et employé pour cette ligne, car il n'y a pas de département avec un X dans son nom pour cette société. C'est difficile avec l'ancienne syntaxe.

C'est pourquoi, entre autres fournisseurs, Microsoft a déconseillé l'ancienne syntaxe de jointure externe, mais pas l'ancienne syntaxe de jointure interne, depuis SQL Server 2005 et versions ultérieures. La seule façon de parler à une base de données exécutée sur Microsoft SQL Server 2005 ou 2008, en utilisant l'ancienne syntaxe de jointure externe, est de définir cette base de données en mode de compatibilité 8.0 (alias SQL Server 2000).

De plus, l'ancienne méthode, en lançant un tas de tables sur l'optimiseur de requête, avec un tas de clauses WHERE, revenait à dire "vous voici, faites de votre mieux". Avec la nouvelle syntaxe, l'optimiseur de requêtes a moins de travail à faire pour comprendre quelles parties vont ensemble.

Alors voilà.

LEFT and INNER JOIN est la vague du futur.

Lasse V. Karlsen
la source
28
"est déconseillé dans la plupart des bases de données modernes." --- juste curieux, lesquels?
zerkms
10
pardonnez-moi, je ne connais pas l'opérateur * =, que fait-il? Merci!
ultrajohn
9
Star = et = Star sont (bien étaient) les jointures externes droite et gauche, ou est-ce gauche et droite? Déconseillé depuis des lustres, je ne les utilise plus depuis SQL Server 6.
Tony Hopkinson
3
La virgule n'est pas déconseillée. La OUTER JOINsyntaxe jamais standard *=/ =*/ *=*est déconseillée.
philipxy
1
Cette réponse ne répond même pas à la question, qui ne concerne pas les jointures externes. La seule affirmation qu'il fait à propos de la virgule vs INNER JOIN ON, concernant l'optimisation, est fausse.
philipxy
17

La syntaxe JOIN conserve les conditions près de la table à laquelle elles s'appliquent. Cela est particulièrement utile lorsque vous joignez un grand nombre de tables.

Soit dit en passant, vous pouvez également effectuer une jointure externe avec la première syntaxe:

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
la source
2
La syntaxe * = est déconseillée dans MS SQLServer et pour une bonne raison: non seulement elle rend la lecture plus difficile, mais elle ne fait pas ce que les gens pensent qu'elle fait et ce n'est PAS la même chose qu'un LEFT JOIN de même apparence. La syntaxe (+) ne m'est pas familière; quelle implémentation SQL fait cela?
Euro Micelli
2
L'autre syntaxe est utilisée par Oracle, au moins.
Lasse V. Karlsen,
4
N'utilisez jamais la syntaxe SQL Server * =, elle ne donnera PAS de résultats cohérents car elle sera parfois interprétée comme une jointure croisée et non comme une jointure gauche. Cela est vrai même depuis SQL Server 2000. Si vous avez du code qui l'utilise, vous devez le corriger.
HLGEM
12

La première est l'ancienne norme. La deuxième méthode a été introduite dans SQL-92, http://en.wikipedia.org/wiki/SQL . La norme complète peut être consultée sur http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt .

Il a fallu de nombreuses années avant que les sociétés de bases de données adoptent la norme SQL-92.

Donc, la raison pour laquelle la deuxième méthode est préférée, c'est la norme SQL selon le comité des normes ANSI et ISO.

Dwight T
la source
,est toujours standard. onne devaient être introduites que pour outer joinune fois les sous-sélections introduites.
philipxy
12

Fondamentalement, lorsque votre clause FROM répertorie des tables comme ceci:

SELECT * FROM
  tableA, tableB, tableC

le résultat est un produit croisé de toutes les lignes des tableaux A, B, C. Ensuite, vous appliquez la restriction WHERE tableA.id = tableB.a_idqui jettera un grand nombre de lignes, puis plus loin ...AND tableB.id = tableC.b_id et vous ne devriez alors obtenir que les lignes qui vous intéressent vraiment dans.

Les SGBD savent comment optimiser ce SQL afin que la différence de performances par rapport à l'écriture à l'aide de JOIN soit négligeable (le cas échéant). L'utilisation de la notation JOIN rend l'instruction SQL plus lisible (à mon humble avis, ne pas utiliser de jointures transforme l'instruction en désordre). En utilisant le produit croisé, vous devez fournir des critères de jointure dans la clause WHERE, et c'est le problème avec la notation. Vous encombrez votre clause WHERE avec des trucs comme

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

qui est uniquement utilisé pour restreindre le produit croisé. La clause WHERE ne doit contenir que des RESTRICTIONS au jeu de résultats. Si vous mélangez des critères de jointure de table avec des restrictions d'ensemble de résultats, vous (et d'autres) trouverez votre requête plus difficile à lire. Vous devez absolument utiliser JOINs et conserver la clause FROM une clause FROM et la clause WHERE une clause WHERE.

Peter Perháč
la source
10

La seconde est préférée car elle est beaucoup moins susceptible d'entraîner une jointure croisée accidentelle en oubliant de mettre dans la clause where. Une jointure sans clause on échouera à la vérification de la syntaxe, une jointure de style ancien sans clause where n'échouera pas, elle fera une jointure croisée.

De plus, lorsque vous devez ultérieurement effectuer une jointure gauche, il est utile pour la maintenance qu'ils soient tous dans la même structure. Et l'ancienne syntaxe est obsolète depuis 1992, il est grand temps d'arrêter de l'utiliser.

De plus, j'ai constaté que de nombreuses personnes qui utilisent exclusivement la première syntaxe ne comprennent pas vraiment les jointures et la compréhension des jointures est essentielle pour obtenir des résultats corrects lors de l'interrogation.

HLGEM
la source
6

Je pense qu'il y a de bonnes raisons sur cette page d'adopter la deuxième méthode - en utilisant des JOIN explicites. Le clinquant est cependant que lorsque les critères JOIN sont supprimés de la clause WHERE, il devient beaucoup plus facile de voir les critères de sélection restants dans la clause WHERE.

Dans les instructions SELECT vraiment complexes, il devient beaucoup plus facile pour un lecteur de comprendre ce qui se passe.

Alan G
la source
5

La SELECT * FROM table1, table2, ...syntaxe est correcte pour quelques tables, mais elle devient de plus en plus exponentiellement ( pas nécessairement une déclaration mathématiquement précise ) à mesure que le nombre de tables augmente.

La syntaxe JOIN est plus difficile à écrire (au début), mais elle rend explicite quels critères affectent quelles tables. Cela rend beaucoup plus difficile de faire une erreur.

De plus, si toutes les jointures sont INNER, les deux versions sont équivalentes. Cependant, au moment où vous avez une jointure EXTERNE n'importe où dans l'instruction, les choses deviennent beaucoup plus compliquées et il est pratiquement garanti que ce que vous écrivez ne demandera pas ce que vous pensez avoir écrit.

Euro Micelli
la source
2

Lorsque vous avez besoin d'une jointure externe, la deuxième syntaxe est pas toujours requise:

Oracle:

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

MSSQLServer (bien qu'il soit obsolète dans la version 2000) / Sybase:

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

Mais revenons à votre question. Je ne connais pas la réponse, mais elle est probablement liée au fait qu'une jointure est plus naturelle (syntaxiquement, au moins) que d'ajouter une expression à une clause where lorsque vous faites exactement cela: rejoindre .

Pablo Santa Cruz
la source
SQL Server a déconseillé cette syntaxe de jointure gauche et même dans SQL Server 2000, il ne donnera pas systématiquement des résultats corrects (parfois il effectue une jointure croisée au lieu d'une jointure gauche) et ne doit jamais être utilisé dans SQL Server.
HLGEM
@HLGEM: Merci pour l'info. Je vais METTRE À JOUR mon message pour refléter ce que vous dites.
Pablo Santa Cruz
0

J'entends beaucoup de gens se plaindre que la première est trop difficile à comprendre et qu'elle n'est pas claire. Je ne vois pas de problème avec cela, mais après avoir eu cette discussion, j'utilise le second même sur INNER JOINS pour plus de clarté.

kemiller2002
la source
1
J'ai été élevé avec l'habitude de ne pas utiliser la syntaxe JOIN et de le faire de la première façon. Je dois admettre que je suis encore souvent pris dans cette habitude juste parce que je pense que mon cerveau a été conditionné pour suivre cette logique, alors que la syntaxe de jointure me semble parfois difficile à penser.
TheTXI
3
On m'a aussi appris de cette façon. J'ai changé mon style de codage, car les gens le regardaient et ne reconnaissaient pas facilement ce qui se passait. Puisqu'il n'y a pas de différence logique et que je ne trouve aucune raison de choisir le premier plutôt que le second, j'ai senti que je devrais m'adapter pour rendre le code plus clair pour aider les autres à comprendre ce que j'écris.
kemiller2002
0

Pour la base de données, ils finissent par être les mêmes. Pour vous, cependant, vous devrez utiliser cette deuxième syntaxe dans certaines situations. Pour éditer des requêtes qui finissent par devoir l'utiliser (découvrir que vous aviez besoin d'une jointure gauche là où vous aviez une jointure droite), et pour des raisons de cohérence, je ne modéliserais que sur la 2ème méthode. Cela facilitera la lecture des requêtes.

Jeff Ferland
la source
0

Eh bien, les première et deuxième requêtes peuvent donner des résultats différents, car une jointure gauche inclut tous les enregistrements de la première table, même s'il n'y a pas d'enregistrements correspondants dans la bonne table.

Gavin H
la source