Wprowadzenie
Podział powiązanych danych na osobne tabele może być korzystny z punktu widzenia spójności, elastyczności i niektórych rodzajów wydajności. Jednak nadal potrzebujesz rozsądnego sposobu ponownej integracji rekordów, gdy odpowiednie informacje obejmują wiele tabel.
W relacyjnych bazach danych połączenia oferują sposób łączenia rekordów w co najmniej dwóch tabelach na podstawie wspólnych wartości pól. Różne typy sprzężeń mogą osiągać różne wyniki w zależności od sposobu obsługi niedopasowanych wierszy. W tym przewodniku omówimy różne typy złączeń oferowanych przez PostgreSQL oraz sposoby ich wykorzystania do łączenia danych tabel z wielu źródeł.
Co to są połączenia?
Krótko mówiąc, dołącza to sposób wyświetlania danych z wielu tabel. Robią to, łącząc ze sobą rekordy z różnych źródeł na podstawie pasujących wartości w określonych kolumnach. Każdy wynikowy wiersz składa się z rekordu z pierwszej tabeli połączonego z wierszem z drugiej tabeli, w oparciu o jedną lub więcej kolumn w każdej tabeli o tej samej wartości.
Podstawowa składnia złączenia wygląda tak:
SELECT *FROM <first_table><join_type> <second_table> <join_condition>;
W sprzężeniu każdy wynikowy wiersz jest tworzony przez uwzględnienie wszystkich kolumn z pierwszej tabeli, po których następują wszystkie kolumny z drugiej tabeli. SELECT
część zapytania może być użyta do określenia dokładnych kolumn, które chcesz wyświetlić.
Z oryginalnych tabel można utworzyć wiele wierszy, jeśli wartości w kolumnach użytych do porównania nie są unikatowe. Na przykład wyobraź sobie, że porównywana jest kolumna z pierwszej tabeli, która zawiera dwa rekordy o wartości „czerwony”. Dopasowana z tym jest kolumna z drugiej tabeli, która ma trzy wiersze z tą wartością. Połączenie wygeneruje sześć różnych wierszy dla tej wartości, reprezentujących różne kombinacje, które można osiągnąć.
Typ sprzężenia i warunki sprzężenia określają sposób konstrukcji każdego wyświetlanego wiersza. Ma to wpływ na to, co dzieje się z wierszami z każdej tabeli, które działają i nie mieć dopasowanie pod warunkiem dołączenia.
Ze względu na wygodę wiele złączeń dopasowuje klucz podstawowy w jednej tabeli do skojarzonego klucza obcego w drugiej tabeli. Chociaż klucze podstawowe i obce są używane tylko przez system bazy danych do zachowania gwarancji spójności, ich relacja często czyni z nich dobrego kandydata na warunki łączenia.
Różne typy złączeń
Dostępne są różne typy złączeń, z których każde potencjalnie da różne wyniki. Zrozumienie, jak skonstruowany jest każdy typ, pomoże ci określić, który jest odpowiedni dla różnych scenariuszy.
Złączenie wewnętrzne
Domyślne połączenie nazywa się sprzężeniem wewnętrznym . W PostgreSQL można to określić za pomocą INNER JOIN
lub po prostu JOIN
.
Oto typowy przykład demonstrujący składnię sprzężenia wewnętrznego:
SELECT *FROM table_1[INNER] JOIN table_2 ON table_1.id = table_2.table_1_id;
Sprzężenie wewnętrzne jest najbardziej restrykcyjnym typem sprzężenia, ponieważ wyświetla tylko wiersze utworzone przez łączenie wierszy z każdej tabeli. Wszelkie wiersze w tabelach składowych, które nie mają pasującego odpowiednika w drugiej tabeli, są usuwane z wyników. Na przykład, jeśli pierwsza tabela ma wartość „niebieski” w kolumnie porównania, a druga tabela nie ma rekordu o tej wartości, ten wiersz zostanie pominięty w danych wyjściowych.
Jeśli przedstawisz wyniki jako diagram Venna tabel komponentów, sprzężenie wewnętrzne umożliwi przedstawienie nakładającego się obszaru dwóch okręgów. Żadna z wartości, które istniały tylko w jednej z tabel, nie jest wyświetlana.
Połączenie lewe
Łączenie lewe to łączenie, które pokazuje wszystkie rekordy znalezione w łączeniu wewnętrznym oraz wszystkie niedopasowane wiersze z pierwszej tabeli. W PostgreSQL można to określić jako LEFT OUTER JOIN
lub jako po prostu LEFT JOIN
.
Podstawowa składnia lewego sprzężenia jest zgodna z następującym wzorcem:
SELECT *FROM table_1LEFT JOIN table_2 ON table_1.id = table_2.table_1_id;
Sprzężenie lewostronne jest konstruowane przez wykonanie sprzężenia wewnętrznego w celu skonstruowania wierszy ze wszystkich pasujących rekordów w obu tabelach. Następnie uwzględniane są również niedopasowane rekordy z pierwszej tabeli. Ponieważ każdy wiersz w łączeniu zawiera kolumny obu tabel, niedopasowane kolumny mają wartość NULL
jako wartość dla wszystkich kolumn w drugiej tabeli.
Jeśli przedstawisz wyniki jako diagram Venna tabel komponentów, lewe sprzężenie pozwoli ci przedstawić cały lewy okrąg. Części lewego okręgu reprezentowane przez przecięcie między dwoma okręgami będą miały dodatkowe dane uzupełnione o prawą tabelę.
Prawe połączenie
Złączenie prawe to złączenie, które pokazuje wszystkie rekordy znalezione w złączeniu wewnętrznym oraz wszystkie niedopasowane wiersze z drugiej tabeli. W PostgreSQL można to określić jako RIGHT OUTER JOIN
lub jako po prostu RIGHT JOIN
.
Podstawowa składnia prawego sprzężenia jest zgodna z następującym wzorcem:
SELECT *FROM table_1RIGHT JOIN table_2 ON table_1.id = table_2.table_1_id;
Prawe sprzężenie jest konstruowane przez wykonanie sprzężenia wewnętrznego w celu skonstruowania wierszy ze wszystkich pasujących rekordów w obu tabelach. Następnie uwzględniane są również niedopasowane rekordy z drugiej tabeli. Ponieważ każdy wiersz w łączeniu zawiera kolumny obu tabel, niedopasowane kolumny mają wartość NULL
jako wartość dla wszystkich kolumn w pierwszej tabeli.
Jeśli przedstawisz wyniki jako diagram Venna tabel komponentów, prawe sprzężenie pozwoli ci przedstawić cały prawy okrąg. Części prawego okręgu reprezentowane przez przecięcie dwóch okręgów będą miały dodatkowe dane uzupełnione o lewą tabelę.
Pełne połączenie
Łączenie pełne to łączenie, które pokazuje wszystkie rekordy znalezione w łączeniu wewnętrznym oraz wszystkie niedopasowane wiersze z obu tabel składowych. W PostgreSQL można to określić jako FULL OUTER JOIN
lub jako po prostu FULL JOIN
.
Podstawowa składnia pełnego sprzężenia jest zgodna z następującym wzorcem:
SELECT *FROM table_1FULL JOIN table_2 ON table_1.id = table_2.table_1_id;
Pełne sprzężenie jest konstruowane przez wykonanie sprzężenia wewnętrznego w celu skonstruowania wierszy ze wszystkich pasujących rekordów w obu tabelach. Następnie uwzględniane są również niedopasowane rekordy z obu tabel. Ponieważ każdy wiersz w łączeniu zawiera kolumny obu tabel, niedopasowane kolumny mają wartość NULL
jako wartość dla wszystkich kolumn w niedopasowanej innej tabeli.
Jeśli przedstawisz wyniki jako diagram Venna tabel komponentów, pełne sprzężenie pozwoli ci całkowicie przedstawić oba okręgi komponentów. Przecięcie dwóch okręgów będzie miało wartości dostarczone przez każdą z tabel składowych. Części okręgów poza nakładającym się obszarem będą miały wartości z tabeli, do której należą, używając NULL
aby wypełnić kolumny znajdujące się w drugiej tabeli.
Połączenie krzyżowe
Specjalne złącze zwane CROSS JOIN
jest również dostępny. Sprzężenie krzyżowe nie używa żadnych porównań w celu określenia, czy wiersze w każdej tabeli pasują do siebie. Zamiast tego wyniki są konstruowane przez proste dodanie każdego wiersza z pierwszej tabeli do każdego wiersza drugiej tabeli.
Daje to iloczyn kartezjański wierszy w dwóch lub więcej tabelach. W efekcie ten styl łączenia bezwarunkowo łączy wiersze z każdej tabeli. Tak więc, jeśli każda tabela ma trzy wiersze, wynikowa tabela będzie miała dziewięć wierszy zawierających wszystkie kolumny z obu tabel.
Na przykład, jeśli masz tabelę o nazwie t1
w połączeniu z tabelą o nazwie t2
, każdy z wierszami r1
, r2
i r3
, wynik będzie składał się z dziewięciu wierszy połączonych w następujący sposób:
t1.r1 + t2.r1t1.r1 + t2.r2t1.r1 + t2.r3t1.r2 + t2.r1t1.r2 + t2.r2t1.r2 + t2.r3t1.r3 + t2.r1t1.r3 + t2.r2t1.r3 + t2.r3
Self-join
Sprzężenie własne to dowolne sprzężenie, które łączy ze sobą wiersze tabeli. Może nie być od razu oczywiste, w jaki sposób może to być przydatne, ale w rzeczywistości ma wiele typowych zastosowań.
Często tabele opisują jednostki, które mogą pełnić wiele ról względem siebie. Na przykład, jeśli masz tabelę people
, każdy wiersz może potencjalnie zawierać mother
kolumna odwołująca się do innych people
na stole. Samodzielne łączenie pozwoliłoby połączyć ze sobą te różne wiersze, łącząc drugą instancję tabeli z pierwszą, w której te wartości pasują.
Ponieważ sprzężenia własne odwołują się do tej samej tabeli dwukrotnie, aliasy tabeli są wymagane do ujednoznacznienia odwołań. Na przykład w powyższym przykładzie możesz dołączyć do dwóch wystąpień people
tabela używająca aliasów people AS children
i people AS mothers
. W ten sposób możesz określić, do której instancji tabeli odwołujesz się podczas definiowania warunków złączenia.
Oto kolejny przykład, tym razem przedstawiający relacje między pracownikami a menedżerami:
SELECT *FROM people AS employeeJOIN people AS manager ON employee.manager_id = manager.id;
Warunki łączenia
Podczas łączenia tabel warunek łączenia określa, w jaki sposób wiersze będą dopasowywane do siebie w celu utworzenia wyników złożonych. Podstawowym założeniem jest zdefiniowanie kolumn w każdej tabeli, które muszą być zgodne, aby łączenie miało miejsce w tym wierszu.
ON
klauzula
Najbardziej standardowym sposobem definiowania warunków łączenia tabel jest ON
klauzula. ON
klauzula używa znaku równości, aby określić dokładne kolumny z każdej tabeli, które będą porównywane w celu określenia, kiedy może wystąpić łączenie. PostgreSQL używa dostarczonych kolumn do łączenia wierszy z każdej tabeli.
ON
klauzula jest najbardziej szczegółowa, ale także najbardziej elastyczna z dostępnych warunków złączenia. Pozwala to na precyzję niezależnie od tego, jak ustandaryzowane są nazwy kolumn w każdej łączonej tabeli.
Podstawowa składnia ON
klauzula wygląda tak:
SELECT *FROM table1JOIN table2ON table1.id = table2.ident;
Tutaj wiersze z table1
i table2
zostanie dołączony za każdym razem, gdy id
kolumna z table1
pasuje do ident
kolumna z table2
. Ponieważ używane jest sprzężenie wewnętrzne, wyniki pokażą tylko te wiersze, które zostały połączone. Ponieważ zapytanie używa symbolu wieloznacznego *
znak, zostaną wyświetlone wszystkie kolumny z obu tabel.
Oznacza to, że zarówno id
kolumna z table1
i ident
kolumna z table2
zostaną wyświetlone, nawet jeśli mają taką samą dokładną wartość z racji spełnienia warunku złączenia. Możesz uniknąć tego powielania, wywołując dokładnie te kolumny, które chcesz wyświetlić w SELECT
lista kolumn.
USING
klauzula
USING
klauzula jest skrótem do określania warunków ON
klauzula, której można użyć, gdy porównywane kolumny mają taką samą nazwę w obu tabelach. USING
klauzula pobiera listę, ujętą w nawiasy, wspólnych nazw kolumn, które należy porównać.
Ogólna składnia USING
klauzula używa tego formatu:
SELECT *FROM table1JOIN table2USING (id, state);
To połączenie łączy table1
z table2
gdy dwie kolumny współdzielone przez obie tabele (id
i state
) każdy ma pasujące wartości.
To samo połączenie może być wyrażone bardziej szczegółowo przy użyciu ON
tak:
SELECT *FROM table1JOIN table2ON table1.id = table2.id AND table1.state = table2.state;
Chociaż oba powyższe sprzężenia spowodowałyby skonstruowanie tych samych wierszy z tymi samymi danymi, byłyby wyświetlane nieco inaczej. Gdy ON
klauzula zawiera wszystkie kolumny z obu tabel, USING
klauzula pomija zduplikowane kolumny. Więc zamiast dwóch oddzielnych id
kolumny i dwa oddzielne state
kolumny (po jednej dla każdej tabeli), wyniki będą zawierały tylko jedną z każdej ze współdzielonych kolumn, a po niej wszystkie inne kolumny dostarczone przez table1
i table2
.
NATURAL
klauzula
NATURAL
klauzula jest kolejnym skrótem, który może jeszcze bardziej zmniejszyć szczegółowość USING
klauzula. NATURAL
dołącz nie określa żadnego kolumny do dopasowania. Zamiast tego PostgreSQL automatycznie połączy tabele na podstawie wszystkich kolumn, które mają pasujące kolumny w każdej bazie danych.
Ogólna składnia NATURAL
klauzula join wygląda tak:
SELECT *FROM table1NATURAL JOIN table2;
Zakładając, że table1
i table2
obie mają kolumny o nazwie id
, state
i company
, powyższe zapytanie byłoby równoważne z tym zapytaniem przy użyciu ON
klauzula:
SELECT *FROM table1JOIN table2ON table1.id = table2.id AND table1.state = table2.state AND table1.company = table2.company;
A to zapytanie przy użyciu USING
klauzula:
SELECT *FROM table1JOIN table2USING (id, state, company);
Podobnie jak USING
klauzula NATURAL
klauzula wyłącza zduplikowane kolumny, więc w wynikach będzie tylko jedna instancja każdej z połączonych kolumn.
Podczas gdy NATURAL
klauzula może zmniejszyć szczegółowość zapytań, należy zachować ostrożność podczas jej używania. Ponieważ kolumny używane do łączenia tabel są obliczane automatycznie, jeśli kolumny w tabelach składowych ulegną zmianie, wyniki mogą się znacznie różnić ze względu na nowe warunki łączenia.
Warunki łączenia i WHERE
klauzula
Warunki łączenia mają wiele cech wspólnych z porównaniami używanymi do filtrowania wierszy danych za pomocą WHERE
klauzule. Obie konstrukcje definiują wyrażenia, których wartością musi być prawda, aby wiersz był brany pod uwagę. Z tego powodu nie zawsze jest intuicyjne, jaka jest różnica między dołączaniem dodatkowych porównań w WHERE
konstruować a nie definiować ich w samej klauzuli join.
Aby zrozumieć różnice, które wynikną, musimy przyjrzeć się kolejności, w jakiej PostgreSQL przetwarza różne części zapytania. W takim przypadku predykaty w warunku sprzężenia są najpierw przetwarzane w celu skonstruowania wirtualnej tabeli sprzężonej w pamięci. Po tym etapie wyrażenia w WHERE
klauzule są oceniane w celu filtrowania wynikowych wierszy.
Jako przykład załóżmy, że mamy dwie tabele o nazwie customer
i order
że musimy się połączyć. Chcemy połączyć te dwie tabele, dopasowując customer.id
kolumna z order.customer_id
kolumna. Dodatkowo interesują nas wiersze w order
tabela, która ma product_id
z 12345.
Biorąc pod uwagę powyższe wymagania mamy dwa warunki, na których nam zależy. Sposób, w jaki wyrażamy te warunki, będzie jednak decydował o otrzymanych wynikach.
Najpierw użyjmy obu jako warunków łączenia dla LEFT JOIN
:
SELECT customer.id AS customer_id, customer.name, order.id AS order_id, order.product_idFROM customerLEFT JOIN orderON customer.id = order.customer_id AND order.product_id = 12345;
Wyniki mogą potencjalnie wyglądać mniej więcej tak:
customer_id | name | order_id | product_id ------------+----------+----------+------------ 4380 | Acme Co | 480 | 12345 4380 | Acme Co | 182 | 12345 320 | Other Co | 680 | 12345 4380 | Acme Co | | 320 | Other Co | | 20 | Early Co | | 8033 | Big Co | |(7 rows)
PostgreSQL osiągnął ten wynik, wykonując następujące operacje:
- Połącz dowolne wiersze w
customer
tabela zorder
tabela gdzie:customer.id
pasuje doorder.customer_id
.order.product_id
pasuje do 12345
- Ponieważ używamy lewego sprzężenia, uwzględnij wszystkie niedopasowane wiersze z lewej tabeli (
customer
), dopełniając kolumny z prawej tabeli (order
) zNULL
wartości. - Wyświetl tylko kolumny wymienione w
SELECT
specyfikacja kolumny.
W rezultacie wszystkie nasze połączone rzędy spełniają oba warunki, których szukamy. Jednak lewe złączenie powoduje, że PostgreSQL uwzględnia również wszystkie wiersze z pierwszej tabeli, które nie spełniły warunku złączenia. Powoduje to powstanie „pozostawionych” wierszy, które wydają się nie odpowiadać widocznej intencji zapytania.
Jeśli przeniesiemy drugie zapytanie (order.product_id
=12345) do WHERE
klauzuli, zamiast uwzględniać ją jako warunek złączenia, otrzymujemy różne wyniki:
SELECT customer.id AS customer_id, customer.name, order.id AS order_id, order.product_idFROM customerLEFT JOIN orderON customer.id = order.customer_idWHERE order.product_id = 12345;
Tym razem wyświetlane są tylko trzy wiersze:
customer_id | name | order_id | product_id ------------+----------+----------+------------ 4380 | Acme Co | 480 | 12345 4380 | Acme Co | 182 | 12345 320 | Other Co | 680 | 12345(3 rows)
Powodem tych różnic jest kolejność wykonywania porównań. Tym razem PostgreSQL przetwarza zapytanie w ten sposób:
- Połącz dowolne wiersze w
customer
tabela zorder
tabela, w którejcustomer.id
pasuje doorder.customer_id
. - Ponieważ używamy lewego sprzężenia, uwzględnij wszystkie niedopasowane wiersze z lewej tabeli (
customer
), dopełniając kolumny z prawej tabeli (order
) zNULL
wartości. - Oceń
WHERE
klauzula usuwająca wszystkie wiersze, które nie mają 12345 jako wartości dlaorder.product_id
kolumna. - Wyświetl tylko kolumny wymienione w
SELECT
specyfikacja kolumny.
Tym razem, mimo że używamy lewego sprzężenia, WHERE
klauzula obcina wyniki przez odfiltrowanie wszystkich wierszy bez poprawnego product_id
. Ponieważ każdy niedopasowany wiersz miałby product_id
ustaw na NULL
, usuwa wszystkie niedopasowane wiersze, które zostały wypełnione przez lewe sprzężenie. Usuwa również wszystkie wiersze, które zostały dopasowane przez warunek złączenia, które nie przeszły tej drugiej rundy kontroli.
Zrozumienie podstawowego procesu używanego przez PostgreSQL do wykonywania zapytań może pomóc w uniknięciu niektórych łatwych do wykonania, ale trudnych do debugowania błędów podczas pracy z danymi.
Wniosek
W tym przewodniku omówiliśmy, w jaki sposób sprzężenia umożliwiają relacyjnym bazom danych łączenie danych z różnych tabel w celu uzyskania bardziej wartościowych odpowiedzi. Rozmawialiśmy o różnych sprzężeniach obsługiwanych przez PostgreSQL, sposobie, w jaki każdy typ zbiera swoje wyniki, oraz o tym, czego można się spodziewać podczas korzystania z określonych rodzajów sprzężeń. Następnie omówiliśmy różne sposoby definiowania warunków złączenia i przyjrzeliśmy się wzajemnemu oddziaływaniu złączeń i WHERE
klauzula może prowadzić do niespodzianek.
Sprzężenia są istotną częścią tego, co sprawia, że relacyjne bazy danych są wystarczająco wydajne i elastyczne, aby obsłużyć tak wiele różnych typów zapytań. Organizowanie danych przy użyciu granic logicznych, a jednocześnie możliwość ponownego łączenia danych w nowatorski sposób w poszczególnych przypadkach, zapewnia relacyjnym bazom danych, takim jak PostgreSQL, niesamowitą wszechstronność. Nauczenie się, jak wykonać to łączenie między tabelami, pozwoli Ci tworzyć bardziej złożone zapytania i polegać na bazie danych, aby tworzyć pełne obrazy Twoich danych.