web-dev-qa-db-fra.com

Clause INNER JOIN ON vs WHERE

Par souci de simplicité, supposons que tous les champs pertinents sont NOT NULL.

Tu peux faire:

SELECT
    table1.this, table2.that, table2.somethingelse
FROM
    table1, table2
WHERE
    table1.foreignkey = table2.primarykey
    AND (some other conditions)

Ou sinon:

SELECT
    table1.this, table2.that, table2.somethingelse
FROM
    table1 INNER JOIN table2
    ON table1.foreignkey = table2.primarykey
WHERE
    (some other conditions)

Est-ce que ces deux fonctionnent de la même manière dans MySQL?

874
JCCyC

INNER JOIN est la syntaxe ANSI que vous devez utiliser.

Il est généralement considéré comme plus lisible, en particulier lorsque vous joignez de nombreuses tables.

Il peut également être facilement remplacé par un OUTER JOIN chaque fois que le besoin s'en fait sentir.

La syntaxe WHERE est davantage axée sur les modèles relationnels.

Un résultat de deux tables JOINed est un produit cartésien des tables auxquelles un filtre est appliqué, qui sélectionne uniquement les lignes dont les colonnes de jointure correspondent.

Il est plus facile de voir cela avec la syntaxe WHERE.

Comme pour votre exemple, dans MySQL (et dans SQL en général), ces deux requêtes sont synonymes.

Notez également que MySQL a également une clause STRAIGHT_JOIN.

A l'aide de cette clause, vous pouvez contrôler l'ordre JOIN: quelle table est analysée dans la boucle externe et quelle est dans la boucle interne.

Vous ne pouvez pas contrôler cela dans MySQL en utilisant la syntaxe WHERE.

674
Quassnoi

D'autres ont souligné qu'INNER JOIN facilite la lisibilité, et c'est une priorité absolue. Je suis d'accord. Laissez-moi essayer d’expliquer pourquoi la syntaxe de la jointure est plus lisible.

Une requête SELECT de base est la suivante:

SELECT stuff
FROM tables
WHERE conditions

La clause SELECT nous dit quoi nous récupérons; la clause FROM nous dit nous l'obtenons, et la clause WHERE nous indique lequel ceux que nous obtenons.

JOIN est une déclaration sur les tables, sur la manière dont elles sont liées (conceptuellement, en fait, dans une seule table). Tous les éléments de requête qui contrôlent les tables - d'où nous obtenons des éléments - appartiennent sémantiquement à la clause FROM (et bien entendu, c'est là que vont les éléments JOIN). Le fait de joindre des éléments dans la clause WHERE confond les qui et les where-from; c'est pourquoi la syntaxe JOIN est préférée.

171
Carl Manaster

Appliquer des instructions conditionnelles dans ON/WHERE

Ici, j'ai expliqué les étapes de traitement d'une requête logique.


Référence: Interrogation T-SQL dans Microsoft® SQL Server ™ 2005
Editeur: Microsoft Press
Date de publication: 07 mars 2006
Imprimer ISBN-10: 0-7356-2313-9
Imprimer ISBN-13: 978-0-7356-2313-2
Pages: 640

Interrogation T-SQL dans Microsoft® SQL Server ™ 2005

(8)  SELECT (9) DISTINCT (11) TOP <top_specification> <select_list>
(1)  FROM <left_table>
(3)       <join_type> JOIN <right_table>
(2)       ON <join_condition>
(4)  WHERE <where_condition>
(5)  GROUP BY <group_by_list>
(6)  WITH {CUBE | ROLLUP}
(7)  HAVING <having_condition>
(10) ORDER BY <order_by_list>

Le premier aspect notable de SQL différent des autres langages de programmation est l’ordre dans lequel le code est traité. Dans la plupart des langages de programmation, le code est traité dans l'ordre dans lequel il est écrit. En SQL, la première clause traitée est la clause FROM, tandis que la clause SELECT, qui apparaît en premier, est traitée presque en dernier.

Chaque étape génère une table virtuelle utilisée comme entrée pour l'étape suivante. Ces tables virtuelles ne sont pas disponibles pour l'appelant (application client ou requête externe). Seule la table générée par l'étape finale est renvoyée à l'appelant. Si une certaine clause n'est pas spécifiée dans une requête, l'étape correspondante est simplement ignorée.

Brève description des phases de traitement des requêtes logiques

Ne vous inquiétez pas trop si la description des étapes ne semble pas avoir beaucoup de sens pour le moment. Celles-ci sont fournies à titre de référence. Les sections qui suivent l'exemple du scénario couvriront les étapes de manière beaucoup plus détaillée.

  1. FROM: un produit cartésien (jointure croisée) est exécuté entre les deux premières tables de la clause FROM et, par conséquent, la table virtuelle VT1 est générée.

  2. ON: le filtre ON est appliqué à VT1. Seules les lignes pour lesquelles <join_condition> est VRAI sont insérées dans VT2.

  3. OUTER (jointure): Si une OUTER JOIN est spécifiée (par opposition à CROSS JOIN ou à INNER JOIN), les lignes de la table préservée ou les tables pour lesquelles aucune correspondance n'a été trouvée sont ajoutées aux lignes de VT2 en tant que lignes externes, ce qui génère VT3. Si plus de deux tables apparaissent dans la clause FROM, les étapes 1 à 3 sont appliquées à plusieurs reprises entre le résultat de la dernière jointure et la table suivante de la clause FROM jusqu'à ce que toutes les tables soient traitées.

  4. WHERE: le filtre WHERE est appliqué à VT3. Seules les lignes pour lesquelles le <where_condition> est VRAI sont insérées dans VT4.

  5. GROUP BY: Les lignes de VT4 sont organisées en groupes basés sur la liste de colonnes spécifiée dans la clause GROUP BY. VT5 est généré.

  6. CUBE | ROLLUP: Les supergroupes (groupes de groupes) sont ajoutés aux lignes de VT5, générant VT6.

  7. HAVING: Le filtre HAVING est appliqué à VT6. Seuls les groupes pour lesquels <having_condition> est VRAI sont insérés dans VT7.

  8. SELECT: La liste SELECT est traitée, générant VT8.

  9. DISTINCT: Les lignes en double sont supprimées de VT8. VT9 est généré.

  10. ORDER BY: Les lignes de VT9 sont triées selon la liste de colonnes spécifiée dans la clause ORDER BY. Un curseur est généré (VC10).

  11. TOP: Le nombre ou le pourcentage de lignes spécifié est sélectionné à partir du début de VC10. La table VT11 est générée et renvoyée à l'appelant.



(L'application d'instructions conditionnelles dans ON/WHERE ne fera pas beaucoup de différence dans quelques cas. Cela dépend du nombre de tables que vous avez jointes et du nombre de lignes disponibles dans chaque table de jointure.)

128
rafidheen

La syntaxe ANSI de jointure implicite est plus ancienne, moins évidente et n'est pas recommandée.

En outre, l'algèbre relationnelle permet l'interchangeabilité des prédicats de la clause WHERE et du INNER JOIN, de sorte que même les requêtes INNER JOIN avec WHERE peuvent être réarrangées par l'optimiseur. .

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

Parfois, cela implique de rendre le INNER JOIN relativement "incomplet" et de mettre certains des critères dans la WHERE simplement pour rendre les listes de critères de filtrage plus faciles à gérer.

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.

60
Cade Roux

Les jointures implicites (c'est ce que l'on appelle votre première requête) deviennent beaucoup plus confuses, difficiles à lire et à maintenir une fois que vous avez besoin d'ajouter de nouvelles tables à votre requête. Imaginez que vous fassiez la même requête et le même type de jointure sur quatre ou cinq tables différentes… c'est un cauchemar.

L'utilisation d'une jointure explicite (votre deuxième exemple) est beaucoup plus lisible et facile à gérer.

29
matt b

Je soulignerai également que l'utilisation de l'ancienne syntaxe est plus sujette aux erreurs. Si vous utilisez des jointures internes sans clause ON, vous obtiendrez une erreur de syntaxe. Si vous utilisez l'ancienne syntaxe et oubliez l'une des conditions de jointure de la clause where, vous obtiendrez une jointure croisée. Les développeurs résolvent souvent ce problème en ajoutant le mot clé distinct (plutôt que de fixer la jointure car ils ne réalisent toujours pas que la jointure est cassée), ce qui peut sembler résoudre le problème, mais ralentit considérablement la requête.

En outre, pour la maintenance, si l'ancienne syntaxe est croisée, comment le mainteneur saura-t-il si vous voulez en avoir une (il existe des situations dans lesquelles des jointures croisées sont nécessaires) ou s'il s'agit d'un accident à réparer?

Laissez-moi vous diriger vers cette question pour voir pourquoi la syntaxe implicite est mauvaise si vous utilisez des jointures à gauche. Sybase * = à Ansi Standard avec 2 tables externes différentes pour la même table interne

De plus (discours personnel ici), la norme utilisant les jointures explicites a plus de 20 ans, ce qui signifie que la syntaxe de jointure implicite est obsolète depuis 20 ans. Voulez-vous écrire un code d'application en utilisant une syntaxe obsolète depuis 20 ans? Pourquoi voulez-vous écrire le code de base de données?

25
HLGEM

Ils ont une signification différente lisible par l'homme.

Toutefois, selon l'optimiseur de requête, ils peuvent avoir la même signification pour la machine.

Vous devriez toujours coder pour être lisible.

Autrement dit, s'il s'agit d'une relation intégrée, utilisez la jointure explicite. Si vous comparez des données faiblement liées, utilisez la clause where.

12
John Gietzen

La norme SQL: 2003 a modifié certaines règles de priorité afin qu'une instruction JOIN soit prioritaire sur une jointure "par virgule". Cela peut réellement changer les résultats de votre requête en fonction de la configuration. Cela pose des problèmes à certaines personnes lorsque MySQL 5.0.12 est passé au respect de la norme.

Ainsi, dans votre exemple, vos requêtes fonctionneraient de la même manière. Mais si vous avez ajouté une troisième table: SELECT ... FROM table1, table2 JOIN table3 ON ... WHERE ...

Avant MySQL 5.0.12, table1 et table2 étaient d'abord liés, puis table3. Maintenant (5.0.12 et après), table2 et table3 sont d'abord joints, puis table1. Cela ne change pas toujours les résultats, mais cela peut et vous ne pouvez même pas vous en rendre compte.

Je n'utilise plus jamais la syntaxe "virgule", optant pour votre deuxième exemple. De toute façon, il est beaucoup plus lisible, les conditions JOIN sont associées aux JOIN, et ne sont pas séparées dans une section de requête distincte.

11
Brent Baisley

Je sais que vous parlez de MySQL, mais enfin: dans Oracle 9, les jointures explicites et implicites généreraient des plans d’exécution différents. Une réponse à ma question qui a été résolue dans Oracle 10+: il n'y a plus de telle différence.

4
João Marcus

La syntaxe de jointure ANSI est nettement plus portable.

Je suis en train de passer à une mise à niveau de Microsoft SQL Server et je mentionnerais également que les syntaxes = * et * = des jointures externes dans SQL Server ne sont pas prises en charge (sans mode de compatibilité) pour les serveurs SQL 2005 et ultérieurs.

1
Benzo

Si vous programmez souvent des procédures stockées dynamiques, vous tomberez amoureux de votre deuxième exemple (en utilisant où). Si vous avez divers paramètres d'entrée et beaucoup de problèmes de morphing, c'est le seul moyen. Sinon, ils exécuteront le même plan de requête, il n'y aura donc aucune différence évidente entre les requêtes classiques.

0
Kviz Majster