Utiliser le mot-clé JOIN ou pas

45

Les requêtes SQL suivantes sont les mêmes:

SELECT column1, column2
FROM table1, table2
WHERE table1.id = table2.id;

SELECT column1, column2
FROM table1 JOIN table2 
ON table1.id = table2.id;

Et aboutissez certainement aux mêmes plans de requête sur chaque SGBD que j'ai jamais essayé.

Mais de temps en temps, je lis ou entends un avis selon lequel l’un est définitivement meilleur que l’autre. Naturellement, ces affirmations ne sont jamais étayées par une explication.

Là où je travaille, la deuxième version semble être favorisée par la majorité des autres développeurs, et je suis donc aussi orientée vers ce style pour minimiser les surprises. Mais dans mon cœur, je pense vraiment au premier (puisque c'est comme ça que je l'ai appris à l'origine).

L'une de ces formes est-elle objectivement meilleure que l'autre? Si non, quelles seraient les raisons pour utiliser l'un sur l'autre?

SingleNegationElimination
la source
1
Pourquoi ne pas le profiler et laisser le reste d'entre nous savoir le résultat? De manière générale, les performances dépassent de loin les préférences de style.
Demian Brecht
3
"résultat dans les mêmes plans de requête sur chaque SGBD que j'ai jamais essayé" Si cela pouvait avoir une réponse en termes de performances, il l'aurait demandé sur stackoverflow.com. hélas, ils sont la même requête.
SingleNegationElimination
Ah .. ça m'a
échappé
2
"Subjective" ne signifie pas "quelle est votre opinion". J'ai édité ceci pour répondre en quelque sorte aux critères énoncés dans la FAQ .
Aaronaught
J'ai aussi tendance à adopter ce style pour minimiser les surprises. Je pense que vous venez de répondre à votre propre question. Les surprises sont mauvaises.
Pieter B

Réponses:

60

Je trouve que la deuxième forme est meilleure. C'est peut-être parce que c'est comme ça que je l'ai appris, je l'avoue, mais j'ai une raison concrète: la séparation des préoccupations. Mettre les champs que vous utilisez pour joindre les tables dans la clause where peut poser des problèmes de compréhension des requêtes.

Par exemple, prenons la requête suivante:

select *
from table1, table2, table3, table4
where table1.id = table2.id
and table2.id = table3.id
and table3.id = table4.id
and table1.column1 = 'Value 1'

La requête ci-dessus a des conditions de jonction de table et des conditions de logique métier réelles combinées dans un seul espace. Avec une requête volumineuse, cela peut être très difficile à comprendre.

Cependant, prenons maintenant ce code:

select *
from table1 join table2 on table1.id = table2.id
join table3 on table2.id = table3.id
join table4 on table3.id = table4.id
where table1.column1 = 'Value 1'

Dans ce cas, tout ce qui concerne les tables ou leur relation est isolé de la clause from, tandis que la logique métier réelle pour la restriction de requête est contenue dans la clause where. Je pense que cela est beaucoup plus compréhensible, en particulier pour les requêtes plus importantes.

Dustin Wilhelmi
la source
C'est la seule façon sensée de le faire, en particulier une fois que vous avez dépassé deux tables ou que vous avez besoin d'une combinaison de jointures gauche, droite et complète.
aglassman
5
+1 Pour "la séparation des préoccupations", les jointures rassemblent des données, où des clauses dictent les sous-ensembles de données qui vous intéressent.
39

La syntaxe de jointure a remplacé l'ancienne syntaxe de virgule en 1992. Il n'y a actuellement aucune raison de jamais écrire du code avec la syntaxe de virgule. Vous ne gagnez rien et vous êtes soumis à certains problèmes que vous n'avez tout simplement pas avec la syntaxe explicite.

En premier lieu, il est très facile de faire une jointure croisée accidentelle en omettant une condition où. C'est quelque chose que la syntaxe de jointure explicite peut empêcher, car vous obtiendrez une erreur de syntaxe.

Si vous envisagez une jointure croisée, la syntaxe de jointure explicite le précisera. Dans la syntaxe implicite, une personne effectuant la maintenance peut supposer que vous avez oublié d'ajouter la clause where.

Il y a ensuite le problème des jointures gauche et droite qui sont problématiques dans au moins quelques dbs utilisant la syntaxe implicite. Ils sont obsolètes dans SQL Server et ne renvoient en réalité pas des résultats corrects, même dans les versions antérieures. Aucune requête nécessitant une jointure externe ne doit contenir la syntaxe implicite dans SQL Server.

De plus, j'ai vu des questions ici et sur d'autres sites où des résultats erronés se produisaient lorsque les utilisateurs mélangeaient les jointures implicites et explicites (lors de l'ajout d'une jointure gauche, par exemple). Il est donc peu judicieux de les mélanger.

Enfin, de nombreuses personnes utilisant des jointures implicites ne comprennent pas réellement les jointures. Ceci est une compréhension critique que vous devez avoir pour interroger efficacement une base de données.

HLGEM
la source
Merci pour l'explication. Quand on m'a enseigné, on nous a montré la syntaxe, mais la différence n'a pas été expliquée. J'ai parfois réussi à produire des requêtes avec des informations manquantes, ce qui aurait franchement augmenté la quantité d'écriture par rapport à la participation explicite.
awiebe
8

Ha. Je viens de trouver une réponse possible à ma propre question en consultant la documentation de PostgreSQL . Pour résumer ce que cette page explique, la requête résultante est toujours la même, mais le nombre de plans que l'optimiseur doit prendre en compte augmente de manière exponentielle avec le nombre de jointures.

Après environ six de ces jointures, le nombre est si important que le temps nécessaire pour planifier la requête peut être perceptible. Après environ dix, l'optimiseur passera d'une recherche exhaustive des plans à une recherche probabiliste et risque de ne pas arriver au plan optimal. .

En définissant un paramètre d'exécution, vous pouvez demander au planificateur de traiter les jointures internes et croisées explicitement mentionnées différemment des jointures implicites, en les forçant à se placer en haut du plan et à ne pas explorer d'autres options.

Il est à noter que le comportement par défaut est le même dans les deux cas et que pour obtenir des plans alternatifs, il est nécessaire de connaître les composants internes des dbms et les particularités des tables en question pour obtenir un résultat différent.

SingleNegationElimination
la source
2
Vous avez cependant légèrement mal compris ces documents. Premièrement, il existe en réalité trois seuils. On déclenche le GEQO comme vous l’avez signalé; les deux autres (limite d'effondrement et de jointure) finissent par contraindre la planeuse à choisir les index applicables plutôt que de réorganiser l'ordre de jointure. Deuxièmement, et tout aussi important, les requêtes sont réécrites au fur et à mesure de leur analyse. Cela a pour résultat que la première des exemples de requêtes est analysée dans le même arbre de requêtes que celui de la seconde - les seuils indiquent alors à PG s'il doit essayer de réorganiser les jointures ou non.
Denis de Bernardy
8

Eh bien voici la théorie de la théorie des ensembles:

Lorsque vous utilisez une virgule pour séparer deux (ou plus) noms de table, vous souhaitez obtenir le produit cartésien. Chaque ligne de la table "de gauche" sera "concaténée" avec celle de la table de droite.

Maintenant, si vous écrivez quelque chose dans la clause where, c'est comme si vous mettiez une condition sur cette "concaténation" en indiquant quelles lignes "concaténer" avec quelles lignes.

Il s’agit en fait de "joindre" les lignes :) et, par conséquent, le mot-clé join qui aide à fournir une syntaxe plus lisible et qui est plus compréhensible que vous "vouliez" vraiment rejoindre certaines valeurs communes. Semblable à ce que @Dustin a clarifié ci-dessus.

Désormais, chaque SGBD est intelligent, c’est-à-dire qu’il ne calcule pas d’abord le produit cartésien puis ne filtre pas les données (ce qui génère un gaspillage extrême), mais le fait plutôt en fonction de la structure de la requête. La seule chose à laquelle je peux penser, c’est que, lorsque vous lui demandez de «rejoindre», c’est comme rendre explicite l’activité de création de lien et vous aide probablement à exécuter le code plus rapidement (de combien? Vous devrez le profiler et voir), mais virgule séparée, il faut un peu de temps pour «déterminer» la stratégie optimale. Je me trompe peut-être, mais je ne fais que supposer de manière éclairée comment coder cela ...

Doctorat
la source
5

Je pense qu'il est généralement préférable d'utiliser les instructions JOIN pour ce cas.

Si, à l’avenir, il se produit une situation nécessitant le remplacement de la déclaration d’une déclaration INNER JOIN par une déclaration OUTER JOIN, il sera beaucoup plus facile à faire avec la deuxième instruction.

Britt Wescott
la source
3

Tous les SGBDR vont les rendre identiques en termes d’exécution. Cela revient à savoir si l’on est plus lisible et plus expressif.

Utilisez la commande JOIN pour indiquer clairement ce qui correspond à la jointure et à la sélection réelle, comme dans:

select name, deptname
from people p, departments d
where p.deptid = d.id and p.is_temp = 'Y'

contre.

select name, deptname
from people p
    inner join departments d on p.deptid = d.id
where p.is_temp = 'Y'

Ce dernier cas indique immédiatement quelle est la condition de jointure et quel est le critère de sélection.

Andy Lester
la source
1

Je n’ai jamais vu les deux résultats dans un ensemble différent d’optimisations et, si la mémoire est mémorisée, c’était dans ms-sql2k lors d’une requête très complexe. Dans cet exemple, l'ancien formulaire utilisé avec * = entraînait des performances 4x plus rapides. Personne, y compris nos techniciens de Microsoft, ne pourrait jamais expliquer pourquoi. Les gars de MS ont qualifié cela d’erreur. Je ne l'ai jamais revu.

Étant donné que la plupart des SGBDR sont suffisamment intelligents pour ne pas traiter tous les cartésiens, la principale raison pour laquelle je peux penser à ne pas l'utiliser (à part qu'il est amorti) est que la plupart des personnes de moins de 30-35 avec lesquelles j'ai travaillé n'ont jamais vu le forme ancienne avant et se perdre terriblement quand ils le rencontrent.

Facture
la source
Bien sûr, cette syntaxe de jointure gauche n'a jamais fourni les résultats corrects de manière fiable (voir BOL pour SQL Server 2000), donc même si elle était plus rapide, je l'aurais remplacée.
HLGEM le
Je n'ai jamais rencontré cela, et la recherche avec l'astérisque ne se termine jamais bien, avez-vous un exemple?
Bill
-1

L'ancien style est obsolète, vous ne devriez pas l'utiliser.

Il ne devrait même pas y avoir de discussion sur la question de savoir si l’on est meilleur ou non. Le nouveau code ne devrait pas utiliser l'ancienne syntaxe.

Pieter B
la source
Je pense que cette réponse n'ajoute rien, sans dire pourquoi elle est déconseillée et ne devrait pas être utilisée.
RemcoGerlich
1
@RemcoGerlich pourquoi il a été déconseillé n'est pas discuté ici. Ce qui est en jeu ici, c'est s'il faut utiliser l'ancienne ou la nouvelle syntaxe. Que l'un soit meilleur que l'autre ou pas, c'est discutable: vous ne devriez pas utiliser l'ancienne syntaxe. La question pourquoi est une autre discussion. (un qui a été réglé il y a 20 ans.)
Pieter B
-4

Une des raisons de la syntaxe plus concise est qu’elle est plus concise, elle est donc plus facile à lire. Je pense que le cas commenté ressemble à l’écriture arithmétique en COBOL, par exemple MULTIPLI A BY B DONNANT C.

John Bickers
la source
Downvoters: Y a-t-il quelque chose de factuel incorrect dans cette réponse, ou étaient-ils simplement "dégoûtés avec vous" des votes négatifs?
Adam Libuša