Jaki jest najlepszy sposób na dwukrotne dołączenie do tego samego stołu?


108

To trochę skomplikowane, ale mam 2 stoły. Powiedzmy, że struktura wygląda mniej więcej tak:

*Table1*
ID
PhoneNumber1
PhoneNumber2

*Table2*
PhoneNumber
SomeOtherField

Tabele można łączyć w oparciu o Tabela1.PhoneNumber1 -> Table2.PhoneNumber lub Table1.PhoneNumber2 -> Table2.PhoneNumber.

Teraz chcę uzyskać zestaw wyników zawierający PhoneNumber1, SomeOtherField, który odpowiada PhoneNumber1, PhoneNumber2 i SomeOtherField, który odpowiada PhoneNumber2.

Pomyślałem o 2 sposobach, aby to zrobić - albo dołączając dwukrotnie do stołu, albo łącząc się raz z klauzulą ​​OR w klauzuli ON.

Metoda 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

To wydaje się działać.

Metoda 2 :

Aby w jakiś sposób uzyskać zapytanie, które wygląda trochę tak -

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

Nie udało mi się jeszcze tego zrobić i nie jestem pewien, czy jest na to sposób.

Jaki jest najlepszy sposób na osiągnięcie tego? Żaden sposób nie wydaje się prosty ani intuicyjny… Czy istnieje prostszy sposób na zrobienie tego? W jaki sposób ten wymóg jest ogólnie wdrażany?

Odpowiedzi:


151

Najpierw spróbuję zmienić te tabele, aby uciec od używania numerów telefonów jako klawiszy naturalnych. Nie jestem fanem naturalnych klawiszy i to jest świetny przykład dlaczego. Naturalne klawisze, zwłaszcza takie jak numery telefonów, mogą się zmieniać i często. Aktualizacja bazy danych, gdy nastąpi ta zmiana, będzie OGROMNYM, podatnym na błędy bólem głowy. *

Metoda 1, jak opisujesz, jest jednak najlepszym rozwiązaniem. Wygląda trochę lakonicznie ze względu na schemat nazewnictwa i krótkie aliasy, ale ... aliasy są twoim przyjacielem, jeśli chodzi o wielokrotne dołączanie do tej samej tabeli lub używanie podzapytań itp.

Po prostu trochę posprzątałbym:

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

Co ja zrobiłem:

  • Nie ma potrzeby określania INNER - wynika to z faktu, że nie określasz LEFT ani RIGHT
  • Nie dodawaj sufiksów do swojej podstawowej tabeli przeglądowej
  • Dodaj sufiks N do aliasów tabel, których będziesz używać wielokrotnie, aby było to oczywiste

* Jednym ze sposobów, w jaki administratorzy baz danych unikają problemów związanych z aktualizacją kluczy naturalnych, jest nieokreślanie kluczy podstawowych i ograniczeń kluczy obcych, co dodatkowo komplikuje problemy związane ze złym projektem bazy danych. Właściwie widziałem to częściej niż nie.


Właśnie zastosowałem to rozwiązanie do własnego problemu. To bardzo pomogło. Jednak zanim to zobaczyłem, zastosowałem klucze podstawowe i klucze obce wszędzie tam, gdzie widziałem, że stoły muszą się ze sobą łączyć. Dlaczego to zły pomysł?
tom pierwszy

6
@volumeone - Myślę, że mogłeś źle zrozumieć ostatnią część mojej odpowiedzi. Klucze podstawowe i obce to dobry pomysł. Unikanie ich to kiepska praktyka, zły projekt i po prostu zły.
Paul Sasik,

Idealnie… ale dlaczego aliasy są koniecznością w tej sytuacji?
Raiden Core

Czy można to zrobić bez dwukrotnego dołączania do tego samego stołu? Może użyj warunku w klauzuli where ...
JohnOsborne

5

Pierwsza jest dobra, chyba że Phone1 lub (bardziej prawdopodobne) phone2 może mieć wartość NULL. W takim przypadku chcesz użyć sprzężenia lewostronnego zamiast sprzężenia wewnętrznego.

Zwykle jest to zły znak, gdy masz stolik z dwoma polami na numery telefonów. Zwykle oznacza to, że projekt bazy danych jest wadliwy.


Świetna uwaga! To spowodowałoby później duże bóle głowy ... dzięki!
froadie

4

Możesz użyć UNIONdo połączenia dwóch sprzężeń:

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
Myślałem o tym, ale potrzebuję, aby został zwrócony jako pojedynczy zdenormalizowany rekord ...
froadie

Och, OK, założyłem coś zupełnie przeciwnego. Jeśli tak, to zrobiłbym to używając czegoś podobnego do twojej pierwszej metody. Zmienię odpowiedź.
Pointy

3

Moim problemem było wyświetlenie rekordu, nawet jeśli nie istnieje żaden numer telefonu lub istnieje tylko jeden numer (pełna książka adresowa). Dlatego użyłem LEFT JOIN, który pobiera wszystkie rekordy z lewej strony, nawet jeśli po prawej stronie nie ma żadnego odpowiednika. U mnie to działa w Microsoft Access SQL (wymagają nawiasów!)

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

Pierwsza metoda to właściwe podejście i zrobi to, czego potrzebujesz. Jednak w przypadku złączeń wewnętrznych wybierzesz wiersze z tylko Table1wtedy, gdy oba numery telefonów istnieją w Table2. Możesz chcieć zrobić LEFT JOINtak, aby wszystkie wiersze z Table1były zaznaczone. Jeśli numery telefonów się nie zgadzają, liczba SomeOtherFields będzie zerowa. Jeśli chcesz się upewnić, że masz co najmniej jeden pasujący numer telefonu, możesz to zrobićWHERE t2.PhoneNumber IS NOT NULL OR t3.PhoneNumber IS NOT NULL

Druga metoda może mieć problem: co się stanie, jeśli Table2ma oba PhoneNumber1i PhoneNumber2? Który wiersz zostanie wybrany? W zależności od danych, kluczy obcych itp. Może to stanowić problem lub nie.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.