Menu

Self join w SQLite: złączenie tabeli z samą sobą

Jak działa self join w SQLite: łączenie w pary wierszy z tej samej tabeli za pomocą aliasów, z przykładami pracownik i kierownik oraz danych hierarchicznych.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Self join to po prostu złączenie z aliasami

W self join nie ma nic szczególnego. To zwykły JOIN, w którym obie strony są akurat tą samą tabelą. Cała sztuczka polega na tym, że SQLite musi jakoś odróżnić obie kopie, więc każdej nadajesz alias.

Sięgasz po niego zawsze, gdy wiersz w tabeli odwołuje się do innego wiersza w tej samej tabeli. Klasyczny przypadek: tabela employees, w której każdy wiersz ma manager_id wskazujące innego pracownika:

Ada nie ma kierownika. Boris i Cleo podlegają Adzie. Diego i Esme podlegają Borisowi. Cała relacja mieści się w jednej tabeli i właśnie tu self join pokazuje, co potrafi.

Podstawowy kształt

Aby połączyć każdego pracownika z imieniem jego kierownika, złącz employees z samą sobą. Jedna kopia gra rolę "pracownika", druga "kierownika":

Czytaj to jak dwie tabele, które akurat dzielą to samo miejsce na dane. e to wiersz pracownika, m to wiersz kierownika. Warunek złączenia e.manager_id = m.id dopasowuje je do siebie: dla każdego pracownika znajdź w m wiersz, którego id pasuje do manager_id pracownika.

Zauważ, że Ady nie ma w wyniku. Jej manager_id to NULL, a INNER JOIN odrzuca wiersze bez dopasowania.

Zachowanie wierszy bez dopasowania: LEFT JOIN

Jeśli chcesz mieć w wyniku wszystkich, łącznie z osobami bez kierownika, przełącz się na LEFT JOIN:

Teraz Ada pojawia się z NULL w kolumnie kierownika. Ta sama mechanika self join, tylko typ złączenia robi to, co LEFT JOIN robi zawsze: zachowuje każdy wiersz z lewej strony i wstawia puste wartości tam, gdzie nie ma dopasowania.

Tej formy zwykle chcesz, gdy wyświetlasz listę osób. "Brak kierownika" to informacja, a odrzucenie wiersza już nie.

Aliasy nie są opcjonalne

Spróbuj złączenia bez aliasów, a SQLite nie będzie wiedzieć, o co ci chodzi:

SELECT name, manager_id FROM employees JOIN employees ON manager_id = id;
-- Error: ambiguous column name: name

Każda kolumna występuje dwa razy, raz z każdej kopii tabeli, i SQLite nie potrafi wybrać. Aliasy rozwiązują to, nadając każdej instancji własną nazwę. Wybieraj aliasy, które opisują rolę wiersza, a nie tabelę:

  • e i m dla pracownika i kierownika.
  • parent i child dla hierarchii.
  • a i b, gdy porównujesz dowolne pary.

To właśnie dzięki aliasom self join czyta się przejrzyście.

Szukanie par w obrębie tabeli

Self join nie służy tylko do hierarchii. Pasuje zawsze, gdy chcesz porównać wiersze w tej samej tabeli. Oto lista produktów, w której szukamy każdej pary o tej samej cenie:

Warto zauważyć dwie rzeczy. Po pierwsze, a.price = b.price to właściwy warunek dopasowania. Po drugie, a.id < b.id sprawia, że zapytanie nie zwraca każdej pary dwa razy (raz jako (Kubek, Zeszyt), drugi raz jako (Zeszyt, Kubek)) ani nie łączy każdego wiersza z nim samym. Ten trik z < warto zapamiętać: pojawia się zawsze, gdy wypisujesz pary.

Dwa poziomy w górę

Self join obsługuje jeden krok w hierarchii. Chcesz kierownika kierownika każdego pracownika? Złącz trzy razy:

Każdy nowy alias oznacza jeden poziom wyżej w drzewie. Działa to dobrze przy dwóch czy trzech krokach, ale szybko się sypie: trzeba by znać głębokość hierarchii w chwili pisania zapytania i dodawać złączenie na każdy poziom. To właśnie ściana, którą przebijają rekurencyjne CTE.

Kiedy nie sięgać po self join

Self join to właściwe narzędzie, gdy w wyniku potrzebujesz kolumn z obu stron relacji. Jeśli chcesz tylko filtrować, na przykład znaleźć każdego pracownika, którego kierownikiem jest Ada, podzapytanie często czyta się lepiej:

Bez żonglowania aliasami, a intencja jest oczywista. Praktyczna zasada: chcesz w wyniku danych z obu wierszy? Self join. Potrzebujesz tylko wartości do porównania? Podzapytanie.

Przy hierarchiach o dowolnej głębokości (schematy organizacyjne, drzewa plików, komentarze w wątkach) żaden z tych wzorców się nie skaluje. To już terytorium rekurencyjnych CTE.

Dalej: podzapytania

Self join i podzapytania rozwiązują częściowo te same problemy, a wiedza, które z nich pasuje, oszczędzi ci później sporo mrużenia oczu nad SQL. Następna strona dokładnie omawia podzapytania (skalarne, skorelowane i w formie IN) oraz to, gdzie każde z nich się sprawdza.

Najczęściej zadawane pytania

Czym jest self join w SQLite?

Self join to zwykły JOIN, w którym tabela jest złączana sama ze sobą. Nadajesz tej samej tabeli dwa różne aliasy, żeby SQLite mógł traktować je jako osobne źródła wierszy, a potem dopasowujesz wiersze po kolumnie, która wiąże jeden wiersz z drugim. Najczęściej to relacja rodzic i dziecko, na przykład pracownik i kierownik.

Dlaczego w self join potrzebne są aliasy?

Bez aliasów SQLite nie wie, o którą kopię tabeli ci chodzi, gdy piszesz nazwę kolumny. Nadanie każdej instancji własnego aliasu (na przykład e dla pracownika i m dla kierownika) pozwala jednoznacznie napisać e.manager_id = m.id. Aliasy nie są opcjonalne: bez nich zapytanie się nie sparsuje.

Kiedy użyć self join, a kiedy podzapytania?

Użyj self join, gdy w wyniku chcesz kolumny z obu wierszy, na przykład imię pracownika i imię kierownika w tej samej linii. Użyj podzapytania, gdy potrzebujesz tylko filtrować albo odczytać jedną wartość. Do głęboko zagnieżdżonych hierarchii nie pasuje żadne z nich: właściwym narzędziem jest rekurencyjne CTE.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ