Condition dans JOIN ou WHERE

194

Y a-t-il une différence (performances, bonnes pratiques, etc.) entre la mise d'une condition dans la clause JOIN et la clause WHERE?

Par exemple...

-- Condition in JOIN
SELECT *
FROM dbo.Customers AS CUS
INNER JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
AND CUS.FirstName = 'John'

-- Condition in WHERE
SELECT *
FROM dbo.Customers AS CUS
INNER JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
WHERE CUS.FirstName = 'John'

Lequel préférez-vous (et peut-être pourquoi)?

Steve Dignan
la source
4
Avez-vous exécuté les deux requêtes? Avez-vous vérifié les plans d'exécution générés par les deux requêtes? Qu'avez-vous observé?
S.Lott
22
@ S.Lott, cette requête est à titre d'exemple uniquement. Je me demande simplement "en général" quelle est la méthode préférée - le cas échéant.
Steve Dignan
1
@Steve Dignan: Vous devriez comparer cela avec des exemples de données et examiner les plans de requête. La réponse sera très, très claire. Et - bonus - vous aurez un morceau de code que vous pourrez réutiliser lorsque des situations plus complexes surviennent.
S.Lott
1
Je mettrais personnellement la condition dans la clause JOIN si la condition décrit la relation. Les conditions génériques qui filtrent simplement le jeu de résultats iraient alors à la partie WHERE. Par exempleFROM Orders JOIN OrderParties ON Orders.Id = OrderParties.Order AND OrderParties.Type = 'Recipient' WHERE Orders.Status = 'Canceled'
Glutexo

Réponses:

154

L'algèbre relationnelle permet l'interchangeabilité des prédicats dans la WHEREclause et le INNER JOIN, de sorte que même les INNER JOINrequêtes avec des WHEREclauses peuvent avoir les prédicats réorganisés par l'optimiseur afin qu'ils puissent déjà être exclus pendant le JOINprocessus.

Je vous recommande d'écrire les requêtes de la manière la plus lisible possible.

Parfois, cela implique de rendre les INNER JOINcritères relativement "incomplets" et d'insérer certains des critères WHEREsimplement pour rendre les listes de critères de filtrage plus faciles à maintenir.

Par exemple, au lieu 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

Écrire:

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

Mais cela dépend, bien sûr.

Cade Roux
la source
7
Il ne s'agit pas seulement de requêtes propres ou de lisibilité, il s'agit de performances. mettre des conditions dans la jointure améliore les performances pour une grande quantité de données avec des tables correctement indexées.
Shahdat
1
Je viens de lancer des rapports de ventes mensuels joignant 5 à 6 tables sur quelques millions d'enregistrements. La performance s'améliore de 30% - serveur sql 2012
Shahdat
2
@Shahdat si vous obtenez une différence de performances aussi importante en déplaçant vos conditions de filtre de la clause where vers la jointure interne, vous devez publier ces plans d'exécution.
Cade Roux
4
@Cade J'ai étudié les plans d'exécution - les deux scénarios montrant le même coût. J'exécute les requêtes plusieurs fois semble prendre le même temps. Auparavant, j'exécutais les requêtes en production et j'obtenais une différence de performances significative car la base de données était utilisée par des utilisateurs en direct. Désolé pour cette confusion.
Shahdat
4
Cette réponse est correcte pour les jointures INNER mais pas pour les jointures gauche / droite.
sotn
123

Pour les jointures internes, je n'ai pas vraiment remarqué de différence (mais comme pour tous les réglages de performances, vous devez vérifier votre base de données dans vos conditions).

Cependant, l'emplacement de la condition fait une énorme différence si vous utilisez des jointures gauche ou droite. Par exemple, considérez ces deux requêtes:

SELECT *
FROM dbo.Customers AS CUS 
LEFT JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
WHERE ORD.OrderDate >'20090515'

SELECT *
FROM dbo.Customers AS CUS 
LEFT JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
AND ORD.OrderDate >'20090515'

Le premier ne vous donnera que les enregistrements dont l'ordre est postérieur au 15 mai 2009, convertissant ainsi la jointure gauche en jointure interne.

Le second donnera ces enregistrements ainsi que tous les clients sans commande. L'ensemble de résultats est très différent selon l'endroit où vous placez la condition. (Sélectionnez * à titre d'exemple uniquement, bien sûr, vous ne devez pas l'utiliser dans le code de production.)

L'exception à cela est lorsque vous souhaitez afficher uniquement les enregistrements dans une table mais pas dans l'autre. Ensuite, vous utilisez la clause where pour la condition et non la jointure.

SELECT *
FROM dbo.Customers AS CUS 
LEFT JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
WHERE ORD.OrderID is null
HLGEM
la source
Merci d'avoir expliqué avec des exemples
Rennish Joseph
1
"convertissant ainsi la jointure gauche en jointure interne". Comment? Pouvez-vous élaborer un peu?
user1451111
@ user1451111 Découvrez ce que renvoie LEFT / RIGHT JOIN: les lignes INNER JOIN plus les lignes de table gauche / droite sans correspondance étendues par NULL. FULL JOIN renvoie les lignes INNER JOIN UNION ALL sans correspondance les lignes de table gauche et droite étendues par NULL. Sachez toujours quel INNER JOIN vous voulez dans le cadre d'une OUTER JOIN. Un WHERE ou ON qui nécessite qu'une colonne éventuellement NULL-étendue ne soit pas NULL après qu'un OUTER JOIN ON supprime toutes les lignes étendues par NULL, c'est-à-dire ne laisse que les lignes INNER JOIN, c'est-à-dire "transforme une OUTER JOIN en INNER JOIN".
philipxy le
1
@ user1451111 ou, en termes plus simples: A left join Best-ce que chaque ligne de A est jointe à chaque ligne correspondante de B. Si B n'a pas de ligne qui correspond, alors les colonnes A ont une valeur mais chaque colonne de B sur cette ligne s'affiche comme des valeurs NULL. Si vous avez écrit, where B.somecolumn = ‘somevalue’alors vous avez un NULL (B.somecolumn) comparé à 'somevalue'. Tout ce qui est comparé à NULL est faux, donc toutes vos lignes où il n'y a pas de ligne B correspondante pour la ligne A, sont éliminées, et les résultats que vous obtenez sont les mêmes que ceux d'une jointure intérieure donnerait, par conséquent la jointure externe est devenue une jointure interne
Caius Jard
oui J'ai vérifié que les résultats sont les mêmes pour: SELECT funds.id, prospects.id FROM prospects de fundsjointure interne sur (prospects.id = funds.lead_id et prospects.is_manual = 'no') et SELECT funds.id, prospects.id FROM fundsleft rejoignez des prospects sur (prospects.id = funds.lead_id) où prospects.is_manual = 'no'
Rohit Dhiman
25

La plupart des produits SGBDR optimiseront les deux requêtes de manière identique. Dans «SQL Performance Tuning» de Peter Gulutzan et Trudy Pelzer, ils ont testé plusieurs marques de SGBDR et n'ont trouvé aucune différence de performances.

Je préfère garder les conditions de jointure séparées des conditions de restriction de requête.

Si vous utilisez OUTER JOINparfois, il est nécessaire de mettre des conditions dans la clause de jointure.

Bill Karwin
la source
1
Je suis d'accord avec vous que la syntaxe est plus propre, et je dois m'en remettre à votre connaissance de ce livre et à votre très haute réputation, mais je peux penser à 4 requêtes au cours de la semaine dernière avec des plans d'exécution, des temps CPU et des lectures logiques très différents lorsque J'ai déplacé où les prédicats à la jointure.
marr75
2
Vous parliez des meilleures pratiques. Dès que vous commencez à tester le fonctionnement d'une implémentation de SGBDR spécifique, d'autres personnes ont donné le bon conseil: benchmark.
Bill Karwin
12

WHERE filtrera après la jointure.

Filtrez sur JOIN pour empêcher l'ajout de lignes pendant le processus JOIN.

TheTXI
la source
10
Sémantiquement, ils sont empêchés pendant le processus INNER JOIN, mais l'optimiseur peut réorganiser les prédicats INNER JOIN et WHERE à volonté, de sorte que l'optimiseur est libre de les exclure plus tard s'il le souhaite.
Cade Roux
1
Cade Roux: C'est vrai. Souvent, ce que vous écrivez en SQL n'est pas ce que l'optimiseur vous donnera quand tout sera dit et fait. Je suppose alors que ce serait juste dans un monde entièrement théorique, alors que votre réponse est bien sûr plus correcte dans le monde des optimiseurs de requêtes automatiques :)
TheTXI
J'aime cette explication de la condition dans leON
Robert Rocha
3

Je préfère le JOIN pour joindre des tables / vues complètes, puis utiliser le WHERE pour introduire le prédicat de l'ensemble résultant.

Il semble syntaxiquement plus propre.

Johnno Nolan
la source
2

Je constate généralement une augmentation des performances lors du filtrage sur la jointure. Surtout si vous pouvez joindre des colonnes indexées pour les deux tables. Vous devriez être en mesure de réduire les lectures logiques avec la plupart des requêtes qui le font également, ce qui, dans un environnement à volume élevé, est un bien meilleur indicateur de performances que le temps d'exécution.

Je suis toujours légèrement amusé quand quelqu'un montre son benchmarking SQL et qu'il a exécuté les deux versions d'un sproc 50 000 fois à minuit sur le serveur de développement et compare les temps moyens.

marr75
la source
0

Mettre la condition dans la jointure me semble «sémantiquement faux», car ce n'est pas pour cela que les JOINs sont «pour». Mais c'est très qualitatif.

Problème supplémentaire: si vous décidez de passer d'une jointure interne à, par exemple, une jointure droite, le fait que la condition se trouve à l'intérieur de la jointure peut entraîner des résultats inattendus.

Jacob B
la source
3
Parfois, ces résultats sont un peu "attendus" et parfois même "intentionnels" (par exemple avec des jointures externes, où la condition WHERE a une sémantique différente de la condition JOIN).
Marcel Toth
0

Les jointures sont à mon avis plus rapides lorsque vous avez une table plus grande. Ce n'est vraiment pas une grande différence, surtout si vous avez affaire à une table un peu plus petite. Quand j'ai découvert les jointures pour la première fois, on m'a dit que les conditions dans les jointures étaient exactement comme les conditions de la clause where et que je pourrais les utiliser de manière interchangeable si la clause where était spécifique à la table sur laquelle appliquer la condition.

Eric
la source
-4

Il est préférable d'ajouter la condition dans la jointure. Les performances sont plus importantes que la lisibilité. Pour les grands ensembles de données, c'est important.

Jeeno Shibu
la source
1
Avez-vous une sorte de preuve, recherchez comment le placement des prédicats mentionnés affecte les performances?
Zso