web-dev-qa-db-fra.com

WHERE Clause vs ON lors de l'utilisation de JOIN

En supposant que j'ai le code T-SQL suivant:

SELECT * FROM Foo f
INNER JOIN Bar b ON b.BarId = f.BarId;
WHERE b.IsApproved = 1;

Le suivant renvoie également le même ensemble de lignes:

SELECT * FROM Foo f
INNER JOIN Bar b ON (b.IsApproved = 1) AND (b.BarId = f.BarId);

Ce n'est peut-être pas le meilleur exemple de cas ici, mais existe-t-il une différence de performance entre ces deux?

41
tugberk

Non, l'optimiseur de requête est suffisamment intelligent pour choisir le même plan d'exécution pour les deux exemples.

Vous pouvez utiliser SHOWPLAN pour vérifier le plan d'exécution.


Néanmoins, vous devriez mettre toutes les connexions de jointure sur la clause ON et toutes les restrictions sur la clause WHERE.

29
aF.

Faites juste attention à la différence avec les jointures externes. Une requête où un filtre de b.IsApproved (sur la table de droite, Bar) est ajouté à la condition ON de la JOIN:

SELECT * 
FROM Foo f 
LEFT OUTER JOIN Bar b ON (b.IsApproved = 1) AND (b.BarId = f.BarId); 

NOT équivaut-il à placer le filtre dans la clause WHERE:

SELECT * 
FROM Foo f 
LEFT OUTER JOIN Bar b ON (b.BarId = f.BarId)
WHERE (b.IsApproved = 1); 

Étant donné que pour 'échoué' des jointures externes à Bar (c'est-à-dire s'il n'y a pas b.BarId pour un f.BarId), ceci laissera b.IsApproved en tant que NULL pour toutes ces lignes de jointure ayant échoué, et ces lignes seront ensuite filtrées. 

Une autre façon de voir les choses est que pour la première requête, LEFT OUTER JOIN Bar b ON (b.IsApproved = 1) AND (b.BarId = f.BarId) retournera toujours les lignes de la table LEFT, puisque LEFT OUTER JOIN garantit que les lignes de la table LEFT seront renvoyées même si la jointure échoue. Cependant, l’ajout de (b.IsApproved = 1) à la condition LEFT OUTER JOIN a pour effet de supprimer toutes les colonnes de la table de droite lorsque (b.IsApproved = 1) est défini sur false, c’est-à-dire selon les mêmes règles que celles appliquées normalement à une condition LEFT JOIN sur (b.BarId = f.BarId).

Update: Pour compléter la question posée par Conrad, la LOJ équivalente pour un filtre OPTIONAL serait:

SELECT * 
FROM Foo f 
LEFT OUTER JOIN Bar b ON (b.BarId = f.BarId)
WHERE (b.IsApproved IS NULL OR b.IsApproved = 1);

en d'autres termes, la clause WHERE doit prendre en compte à la fois la condition d'échec de la jointure (NULL) et le filtre à ignorer, ainsi que les conditions dans lesquelles la jointure aboutit et le filtre doit être appliqué. (b.IsApproved ou b.BarId pourrait être testé pour NULL)

J'ai mis un SqlFiddle ensemble ici qui montre les différences entre les divers emplacements du filtre b.IsApproved par rapport à la JOIN.

43
StuartLC
SELECT * FROM Foo f
INNER JOIN Bar b ON b.BarId = f.BarId
WHERE b.IsApproved = 1;

C'est la meilleure forme pour aller. Il est facile à lire et à modifier. Dans le monde des affaires, c'est ce que vous voudriez faire. En ce qui concerne les performances, elles sont identiques.

6
Landin Martens

Je viens de tester une requête sur quatre tables - une table principale avec trois INNER JOINs et un total de quatre paramètres, et comparé les plans d'exécution des deux approches la clause WHERE).

Les plans d'exécution sont exactement les mêmes. J'ai exécuté ceci sur SQL Server 2008 R2.

0
Jason L

J'ai eu quelques cas où l'optimiseur n'était pas assez intelligent, même sur les versions récentes de MSSQL - et la différence de performances était énorme.

Mais il s’agit d’une exception. La plupart du temps, l’optimiseur SQL Server résoudra le problème et obtiendra le bon plan.

Donc, maintenez la politique d’utilisation des filtres sur la clause WHERE et optimisez-le si nécessaire.

0
Fabricio Araujo