Quelle est la meilleure façon de rejoindre deux fois la même table?

108

C'est un peu compliqué, mais j'ai 2 tables. Disons que la structure est quelque chose comme ceci:

*Table1*
ID
PhoneNumber1
PhoneNumber2

*Table2*
PhoneNumber
SomeOtherField

Les tables peuvent être jointes en fonction de Table1.PhoneNumber1 -> Table2.PhoneNumber ou Table1.PhoneNumber2 -> Table2.PhoneNumber.

Maintenant, je souhaite obtenir un jeu de résultats contenant PhoneNumber1, SomeOtherField qui correspond à PhoneNumber1, PhoneNumber2 et SomeOtherField qui correspond à PhoneNumber2.

J'ai pensé à 2 façons de le faire - soit en se joignant à la table deux fois, soit en se joignant une fois avec un OU dans la clause ON.

Méthode 1 :

SELECT t1.PhoneNumber1, t1.PhoneNumber2, 
   t2.SomeOtherFieldForPhone1, t3.someOtherFieldForPhone2
FROM Table1 t1
INNER JOIN Table2 t2
   ON t2.PhoneNumber = t1.PhoneNumber1
INNER JOIN Table2 t3
   ON t3.PhoneNumber = t1.PhoneNumber2

Cela semble fonctionner.

Méthode 2 :

Pour avoir une requête qui ressemble un peu à ceci -

SELECT ...
FROM Table1
INNER JOIN Table2 
   ON Table1.PhoneNumber1 = Table2.PhoneNumber OR
      Table1.PhoneNumber2 = Table2.PhoneNumber

Je n'ai pas encore réussi à faire fonctionner cela et je ne sais pas s'il existe un moyen de le faire.

Quelle est la meilleure façon d'y parvenir? Aucun des deux moyens ne semble simple ou intuitif ... Y a-t-il un moyen plus simple de le faire? Comment cette exigence est-elle généralement mise en œuvre?

froadie
la source

Réponses:

151

Tout d'abord, j'essaierais de refactoriser ces tableaux pour éviter d'utiliser les numéros de téléphone comme clés naturelles. Je ne suis pas un fan des touches naturelles et c'est un excellent exemple. Les touches naturelles, en particulier des éléments tels que les numéros de téléphone, peuvent changer et fréquemment. La mise à jour de votre base de données lorsque ce changement se produit sera un énorme casse-tête sujet aux erreurs. *

La méthode 1 telle que vous la décrivez est cependant votre meilleur pari. Cela semble un peu laconique en raison du schéma de dénomination et des alias courts, mais ... l'alias est votre ami lorsqu'il s'agit de rejoindre la même table plusieurs fois ou d'utiliser des sous-requêtes, etc.

Je voudrais juste nettoyer un peu les choses:

SELECT t.PhoneNumber1, t.PhoneNumber2, 
   t1.SomeOtherFieldForPhone1, t2.someOtherFieldForPhone2
FROM Table1 t
JOIN Table2 t1 ON t1.PhoneNumber = t.PhoneNumber1
JOIN Table2 t2 ON t2.PhoneNumber = t.PhoneNumber2

Ce que j'ai fait:

  • Pas besoin de spécifier INNER - c'est implicite par le fait que vous ne spécifiez pas LEFT ou RIGHT
  • Ne pas ajouter de suffixe n à votre table de recherche principale
  • N-Suffixez les alias de table que vous utiliserez plusieurs fois pour le rendre évident

* Une façon pour les administrateurs de base de données d'éviter les maux de tête liés à la mise à jour des clés naturelles est de ne pas spécifier les clés primaires et les contraintes de clé étrangère, ce qui aggrave encore les problèmes liés à une mauvaise conception de la base de données. J'ai en fait vu cela plus souvent qu'autrement.

Paul Sasik
la source
Je viens d'utiliser cette solution pour mon propre problème. Cela a beaucoup aidé. Cependant, avant de voir cela, j'ai appliqué des clés primaires et des clés étrangères partout où je pouvais voir des tables devant se joindre les unes aux autres. pourquoi est-ce une mauvaise idee?
volume un
6
@volumeone - Je pense que vous avez peut-être mal compris la dernière partie de ma réponse. Les clés primaires et étrangères sont une bonne idée. Les éviter est une mauvaise pratique, une mauvaise conception et tout simplement une mauvaise.
Paul Sasik
Parfait..mais pourquoi les alias sont indispensables dans cette situation?
Raiden Core
Existe-t-il un moyen de faire cela sans rejoindre la même table deux fois? Peut-être utiliser une condition dans la clause where ...
JohnOsborne
5

Le premier est bon à moins que Phone1 ou (plus probablement) phone2 ne soit nul. Dans ce cas, vous souhaitez utiliser une jointure gauche au lieu d'une jointure interne.

C'est généralement un mauvais signe lorsque vous avez une table avec deux champs de numéro de téléphone. Habituellement, cela signifie que la conception de votre base de données est défectueuse.

HLGEM
la source
Bon point! Cela m'aurait causé de gros maux de tête plus tard ... merci!
froadie
4

Vous pouvez utiliser UNIONpour combiner deux jointures:

SELECT Table1.PhoneNumber1 as PhoneNumber, Table2.SomeOtherField as OtherField
  FROM Table1
  JOIN Table2
    ON Table1.PhoneNumber1 = Table2.PhoneNumber
 UNION
SELECT Table1.PhoneNumber2 as PhoneNumber, Table2.SomeOtherField as OtherField
  FROM Table1
  JOIN Table2
    ON Table1.PhoneNumber2 = Table2.PhoneNumber
Pointu
la source
1
J'y ai pensé, mais j'ai besoin qu'il soit retourné comme un seul enregistrement dénormalisé ...
froadie
Oh OK, j'ai supposé à peu près le contraire. Si tel est le cas, je le ferais en utilisant quelque chose comme votre première méthode. Je modifierai ma réponse.
Pointy
3

Mon problème était d' afficher l'enregistrement même s'il n'y a pas ou un seul numéro de téléphone (carnet d'adresses complet). Par conséquent, j'ai utilisé une jointure à gauche qui prend tous les enregistrements de la gauche, même si aucune correspondance n'existe à droite. Pour moi, cela fonctionne dans Microsoft Access SQL (ils nécessitent la parenthèse!)

SELECT t.PhoneNumber1, t.PhoneNumber2, t.PhoneNumber3
   t1.SomeOtherFieldForPhone1, t2.someOtherFieldForPhone2, t3.someOtherFieldForPhone3
FROM 
(
 (
  Table1 AS t LEFT JOIN Table2 AS t3 ON t.PhoneNumber3 = t3.PhoneNumber
 )
 LEFT JOIN Table2 AS t2 ON t.PhoneNumber2 = t2.PhoneNumber
)
LEFT JOIN Table2 AS t1 ON t.PhoneNumber1 = t1.PhoneNumber;
F1iX
la source
2

La première méthode est la bonne approche et fera ce dont vous avez besoin. Cependant, avec les jointures internes, vous ne sélectionnerez des lignes que Table1si les deux numéros de téléphone existent dans Table2. Vous souhaiterez peut-être faire une LEFT JOINpour que toutes les lignes de Table1soient sélectionnées. Si les numéros de téléphone ne correspondent pas, le SomeOtherFields serait nul. Si vous voulez vous assurer d'avoir au moins un numéro de téléphone correspondant, vous pouvez le faireWHERE t2.PhoneNumber IS NOT NULL OR t3.PhoneNumber IS NOT NULL

La deuxième méthode pourrait avoir un problème: que se passe-t-il si Table2a les deux PhoneNumber1et PhoneNumber2? Quelle ligne sera sélectionnée? Selon vos données, clés étrangères, etc., cela peut ou non être un problème.

Nelson Rothermel
la source