web-dev-qa-db-fra.com

SQL joint gauche vs plusieurs tables sur la ligne FROM?

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)?

242
jmucchiello

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

Ce n'est pas juste pour 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 départements et tous leurs employés. Notez que certaines entreprises ne disposent pas encore de départements, 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.

Alors tu fais ça:

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 répondre aux critères voulant que vous ne vouliez 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. Laissez-moi expliquer.

Si l'optimiseur de requêtes détermine que la solution consiste à prendre d'abord une entreprise, puis à rechercher les services, puis à effectuer une jointure interne avec les employés, vous ne obtiendrez aucune entreprise ne disposant pas de services.

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

Et dans ce cas, en raison de la jointure gauche, la colonne Department.ID sera NULL et, par conséquent, lorsqu'il s'agira de INNER JOIN to Employee, il n'y a aucun moyen de respecter cette contrainte pour la ligne Employee et elle ne le sera pas. apparaître.

D'autre part, si l'optimiseur de requête décide d'aborder la jointure département-employé, puis effectuez une jointure à gauche avec les entreprises, vous les verrez.

Donc, l'ancienne syntaxe est ambiguë. Il n'y a aucun moyen de spécifier ce que vous voulez, sans s'occuper des indicateurs de requête, et certaines bases de données n'ont aucun moyen.

Entrez la nouvelle syntaxe, avec cela, vous pouvez choisir.

Par exemple, si vous souhaitez que toutes les entreprises, comme indiqué dans la description du problème, écrivez ceci:

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

Ici, vous indiquez que vous voulez que la jointure département-employé soit faite en une seule jointure, puis que vous laissiez rejoindre les résultats avec les entreprises.

De plus, supposons que vous souhaitiez uniquement 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épartement 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. La ligne peut donc apparaître avec des informations sur la société, mais peut contenir des valeurs NULL dans toutes les colonnes de département et d'employé de cette ligne, car aucun département ne possède 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é d'utiliser l'ancienne syntaxe de jointure externe, mais pas l'ancienne, à partir de SQL Server 2005. La seule façon de parler à une base de données s'exécutant sous Microsoft SQL Server 2005 ou 2008, à l'aide de l'ancienne syntaxe de jointure externe de style ancien, consiste à définir cette base de données en mode de compatibilité 8.0 (également appelé SQL Server 2000).

De plus, l'ancienne méthode, consistant à lancer un ensemble de tables sur l'optimiseur de requêtes, avec un ensemble de clauses WHERE, s'apparentait à "vous êtes ici, faites de votre mieux". Avec la nouvelle syntaxe, l'optimiseur de requêtes a moins de travail à faire pour déterminer quelles parties vont ensemble.

Donc là vous l'avez.

LEFT and INNER JOIN est la vague du futur.

La syntaxe JOIN maintient les conditions à proximité de la table à laquelle elles s'appliquent. Ceci est particulièrement utile lorsque vous rejoignez un grand nombre de tables.

À propos, vous pouvez aussi faire 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)
16
Andomar

En gros, quand votre clause FROM liste les tables comme ceci:

SELECT * FROM
  tableA, tableB, tableC

le résultat est un produit croisé de toutes les lignes des tables A, B, C. Ensuite, vous appliquez la restriction WHERE tableA.id = tableB.a_id qui éliminera un grand nombre de lignes, puis plus loin ... AND tableB.id = tableC.b_id et vous devrait alors n'obtenir que les lignes qui vous intéressent vraiment.

Les SGBD savent comment optimiser ce SQL afin que la différence de performances entre cette écriture à l'aide de JOINs soit négligeable (le cas échéant). L’utilisation de la notation JOIN rend l’instruction SQL plus lisible (IMHO, l’absence de jointure 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 de la notation. Vous remplissez votre clause WHERE avec des choses 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 les autres) aurez plus de difficulté à lire votre requête. Vous devez absolument utiliser JOIN et conserver la clause FROM comme clause FROM et la clause WHERE comme clause WHERE.

11
Peter Perháč

Le premier moyen est l'ancien standard. 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 à l’adresse suivante: 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 le standard SQL selon le comité des normes ANSI et ISO.

11
Dwight T

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

De plus, lorsque vous devez joindre plus tard à gauche, il est utile, pour la maintenance, que tous soient dans la même structure. Et l'ancienne syntaxe était périmée 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 que la compréhension des jointures est essentielle pour obtenir des résultats corrects lors de l'interrogation.

9
HLGEM

Je pense qu'il y a quelques bonnes raisons sur cette page pour adopter la deuxième méthode - l'utilisation de JOIN explicites. Cependant, le fait est 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 le lecteur de comprendre ce qui se passe.

6
Alan G

La syntaxe SELECT * FROM table1, table2, ... est acceptable pour quelques tables, mais elle devient exponentielle (pas nécessairement une déclaration mathématiquement exacte) de plus en plus lisible lorsque le nombre de tables augmente.

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

De même, si toutes les jointures sont INNER, les deux versions sont équivalentes. Cependant, au moment où vous avez une jointure OUTER n'importe où dans la déclaration, 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.

5
Euro Micelli

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

Oracle:

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

MSSQLServer (bien que ce 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 lorsque vous faites exactement cela: rejoindre .

2
Pablo Santa Cruz

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

0
kemiller2002

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

0
Jeff Ferland

La première et la deuxième requête 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 table de droite.

0
Gavin H