web-dev-qa-db-fra.com

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

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

*Table1*
ID
PhoneNumber1
PhoneNumber2

*Table2*
PhoneNumber
SomeOtherField

Les tables peuvent être jointes sur la base de Table1.PhoneNumber1 -> Table2.PhoneNumber ou Table1.PhoneNumber2 -> Table2.PhoneNumber.

Maintenant, je veux 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 joignant deux fois sur la table, soit en joignant une fois avec un OR 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 en quelque sorte 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.

Quel est le meilleur moyen d'y parvenir? Aucune solution ne semble simple ni intuitive ... Existe-t-il un moyen plus simple de procéder? Comment cette exigence est-elle généralement mise en œuvre?

97
froadie

Premièrement, j'essaierais de refactoriser ces tables pour éviter d'utiliser des numéros de téléphone comme des clés naturelles. Je ne suis pas un fan des clés naturelles et c’est un bon exemple. Les clés naturelles, notamment les numéros de téléphone, peuvent changer fréquemment. Mettre à jour votre base de données lorsque ce changement se produira sera un mal de tête énorme, sujet aux erreurs. *

Méthode 1 comme vous le décrivez, c'est votre meilleur pari cependant. Le schéma de nommage et les alias courts donnent une impression de lacune, mais l'alias est votre ami quand il s'agit de joindre 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 du fait que vous ne spécifiez pas LEFT ou RIGHT
  • Ne pas suffixer votre table de recherche principale
  • N-Suffixe les alias de table que vous utiliserez plusieurs fois pour le rendre évident

* Pour éviter les problèmes liés à la mise à jour des clés naturelles, les administrateurs de bases de données évitent de spécifier des clés primaires et des contraintes de clé étrangère, ce qui aggrave encore les problèmes liés à une conception médiocre de la base de données. J'ai effectivement vu cela plus souvent qu'autrement.

135
Paul Sasik

La première est valable à 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. Cela signifie généralement que la conception de votre base de données est défectueuse.

5
HLGEM

Mon problème était de afficher l'enregistrement même s'il n'existe pas ou un seul numéro de téléphone (carnet d'adresses complet). C’est pourquoi j’ai utilisé un LEFT JOIN qui prend tous les enregistrements de la gauche, même s’il n’existe pas de correspondance à droite. Pour moi, cela fonctionne dans Microsoft Access SQL (ils ont besoin de 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;
2
F1iX

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 que les lignes de Table1 si les deux numéros de téléphone existent dans Table2. Vous voudrez peut-être faire un LEFT JOIN pour que toutes les lignes de Table1 sont sélectionnés. Si les numéros de téléphone ne correspondent pas, le SomeOtherFields sera nul. Si vous voulez vous assurer que vous avez au moins un numéro de téléphone correspondant, vous pouvez alors faire WHERE t2.PhoneNumber IS NOT NULL OR t3.PhoneNumber IS NOT NULL

La deuxième méthode pourrait avoir un problème: que se passera-t-il si Table2 a les deux PhoneNumber1 et PhoneNumber2? Quelle ligne sera sélectionnée? Selon vos données, clés étrangères, etc., cela peut poser problème ou non.

2
Nelson Rothermel

Vous pouvez utiliser UNION pour 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
2
Pointy