Qual è il modo migliore per partecipare due volte allo stesso tavolo?


108

Questo è un po 'complicato, ma ho 2 tavoli. Diciamo che la struttura è qualcosa del genere:

*Table1*
ID
PhoneNumber1
PhoneNumber2

*Table2*
PhoneNumber
SomeOtherField

Le tabelle possono essere unite in base a Table1.PhoneNumber1 -> Table2.PhoneNumber o Table1.PhoneNumber2 -> Table2.PhoneNumber.

Ora, voglio ottenere un set di risultati che contiene PhoneNumber1, SomeOtherField che corrisponde a PhoneNumber1, PhoneNumber2 e SomeOtherField che corrisponde a PhoneNumber2.

Ho pensato a 2 modi per farlo: unendoti al tavolo due volte o unendoti una volta con un OR nella clausola ON.

Metodo 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

Sembra funzionare.

Metodo 2 :

Per avere in qualche modo una query che assomigli un po 'a questa:

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

Non l'ho ancora fatto funzionare e non sono sicuro che ci sia un modo per farlo.

Qual è il modo migliore per farlo? Nessuno dei due modi sembra semplice o intuitivo ... C'è un modo più diretto per farlo? Come viene generalmente implementato questo requisito?

Risposte:


151

Innanzitutto, proverei a eseguire il refactoring di queste tabelle per evitare di utilizzare i numeri di telefono come chiavi naturali. Non sono un fan delle chiavi naturali e questo è un ottimo esempio del perché. I tasti naturali, in particolare cose come i numeri di telefono, possono cambiare e spesso così. L'aggiornamento del database quando si verifica tale modifica sarà un enorme mal di testa soggetto a errori. *

Il metodo 1 come lo descrivi è comunque la soluzione migliore. Sembra un po 'conciso a causa dello schema di denominazione e degli alias brevi ma ... l'aliasing è tuo amico quando si tratta di unirsi alla stessa tabella più volte o utilizzare sottoquery ecc.

Vorrei solo pulire un po 'le cose:

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

Cosa ho fatto:

  • Non è necessario specificare INNER - è implicito dal fatto che non specifichi LEFT o RIGHT
  • Non suffisso n nella tabella di ricerca principale
  • N-Suffisso agli alias di tabella che userete più volte per renderlo ovvio

* Un modo in cui gli amministratori di database evitano il mal di testa dell'aggiornamento delle chiavi naturali è di non specificare chiavi primarie e vincoli di chiavi esterne, il che aggrava ulteriormente i problemi con una cattiva progettazione del database. In realtà l'ho visto il più delle volte.


Ho appena usato questa soluzione per il mio problema. Ha aiutato molto. Tuttavia, prima di vedere questo, ho applicato chiavi primarie e chiavi esterne ovunque potessi vedere le tabelle che dovevano unirsi tra loro. Perché questa è una cattiva idea?
volume uno

6
@volumeone - Penso che tu possa aver frainteso l'ultima parte della mia risposta. Le chiavi primarie ed esterne sono una buona idea. Evitarli è una cattiva pratica, un cattivo design e semplicemente un male.
Paul Sasik

Perfetto..ma perché gli alias sono un must in questa situazione?
Raiden Core,

C'è un modo per farlo senza partecipare due volte allo stesso tavolo? Forse utilizzare una condizione nella clausola where ...
JohnOsborne

5

Il primo è valido a meno che Phone1 o (più probabilmente) phone2 non siano nulli. In tal caso, si desidera utilizzare un join sinistro invece di un join interno.

Di solito è un brutto segno quando hai una tabella con due campi di numeri di telefono. Di solito questo significa che la progettazione del database è difettosa.


Ottimo punto! Questo mi avrebbe causato grossi mal di testa in seguito ... grazie!
froadie

4

Puoi usare UNIONper combinare due join:

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

1
Ci ho pensato, ma ho bisogno che venga restituito come un singolo record denormalizzato ...
froadie

Oh OK, ho pensato praticamente il contrario. Se è così, lo farei usando qualcosa come il tuo primo metodo. Modificherò la mia risposta.
Pointy

3

Il mio problema era quello di visualizzare il record anche se non esisteva nessuno o solo un numero di telefono (rubrica completa). Pertanto ho utilizzato un LEFT JOIN che prende tutti i record da sinistra, anche se non esiste alcun corrispondente a destra. Per me questo funziona in Microsoft Access SQL (richiedono le parentesi!)

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

Il primo metodo è l'approccio corretto e farà ciò di cui hai bisogno. Tuttavia, con gli inner join, selezionerai solo le righe da Table1se entrambi i numeri di telefono esistono inTable2 . Potresti voler fare in LEFT JOINmodo che tutte le righe da Table1siano selezionate. Se i numeri di telefono non corrispondono, il fileSomeOtherField s sarebbe nulla. Se vuoi assicurarti di avere almeno un numero di telefono corrispondente, puoi farloWHERE t2.PhoneNumber IS NOT NULL OR t3.PhoneNumber IS NOT NULL

Il secondo metodo potrebbe avere un problema: cosa succede se Table2ha sia PhoneNumber1e PhoneNumber2? Quale riga verrà selezionata? A seconda dei dati, delle chiavi esterne, ecc., Questo può o non può essere un problema.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.