13. Łączenie tabel

13. Łączenie tabel

Autor: Grzegorz Chuchra

Opublikowano: 3/24/2006, 12:00 AM

Liczba odsłon: 68709

Pobieranie danych z kilku niezależnych tabel jest rzeczą tak oczywistą, że można się zdziwić dlaczego zajmujemy się tą kwestią tak późno. A to dlatego, że aby poprawnie używać złączeń, musieliśmy zrozumieć, na jakiej zasadzie działa mechanizm wybierania danych z pojedynczych tabel oraz jak efektywnie formatować przedstawione wyniki.
Na początku chciałbym przybliżyć wszystkim czym tak naprawdę są złączenia. Są niczym innym, jak spojeniem danych z kilku tabel w jedną tabelę wynikową. Realizowane są poprzez porównanie jednej lub kilku kolumn z jednej tabel z innymi kolumnami znajdującymi się w drugiej tabeli. Najlepiej jednak zademonstrować to na przykładzie.
Jeśli chcielibyśmy zobaczyć jakie stanowisko w firmie piastuje dana osoba, musielibyśmy połączyć tabelkę Contact z Employee. W tabeli Employee mamy dane dotyczące stanowiska w firmie (kolumna Title), ale nic nam z tych danych, jeśli nie będziemy wiedzieli kogo one dotyczą. Tą informację z kolei mamy w tabeli Contact. Wiemy jedynie, że częścią wspólną jest tutaj kolumna ContactID. Jest ona kluczem głównym w tabeli Contact i obcym w Employee. Ta informacja wystarcza nam, aby wydobyć interesujące nas informacje.

SELECT Person.Contact.FirstName, Person.Contact.LastName, HumanResources.Employee.Title
FROM   Person.Contact INNER JOIN
       HumanResources.Employee ON Person.Contact.ContactID = HumanResources.Employee.ContactID

Wynikiem zapytania jest tabela przyporządkowująca każdemu kontaktowi stanowisko, jakie osoba posiadająca dany kontakt piastuje. Składnia złączenia na pierwszy rzut oka nie jest zachęcająca, ale można tu zauważyć pewien szablon. W sekcji FROM znajduje się nazwa pierwszej tabeli którą chcemy połączyć, potem następuje słowa kluczowe INNER JOIN i nazwa drugiej tabeli. Następnie następuje znacznik ON i najważniejszy fragment złączenia polegający na wypisaniu kolumn, po których i w jaki sposób chcemy złączać tabele. W przypadku poprawnie znormalizowanej i zaprojektowanej bazy danych zdecydowana większość złączeń będzie odbywała się po kluczach głównych i obcych. Gwarancją takich złączeń jest szybkie wykonanie selektów, a co za tym idzie prezentacji wyników.
Bardzo istotną informacją jest fakt, że nasza tabela wynikowa posiada wszelkie cechy zwykłej tabeli. Możemy dowolnie wybierać kolumny, które chcemy wyświetlać, zmiana dotyczy sytuacji gdy kolumny się powtarzają - wtedy należy poprzedzać je nazwą tabeli. Możemy dokonywać operacji filtracji, grupowania oraz używać klauzuli ORDER BY. Dodatkowo można używać złączeń dowolną ilość razy. Jedynym ograniczeniem jest szybkość działania zapytania. Sprawdźmy więc jaka była historia zatrudnienia w firmie pani Sheela Word.

SELECT Person.Contact.FirstName, Person.Contact.LastName, HumanResources.Employee.Title,
       HumanResources.Department.Name, HumanResources.EmployeeDepartmentHistory.StartDate,
       HumanResources.EmployeeDepartmentHistory.EndDate
FROM   Person.Contact INNER JOIN
       HumanResources.Employee ON Person.Contact.ContactID = HumanResources.Employee.ContactID INNER JOIN
       HumanResources.EmployeeDepartmentHistory ON
       HumanResources.Employee.EmployeeID = HumanResources.EmployeeDepartmentHistory.EmployeeID INNER JOIN
       HumanResources.Department ON HumanResources.EmployeeDepartmentHistory.DepartmentID = HumanResources.Department.DepartmentID
WHERE  (Person.Contact.ContactID = 1037)
ORDER BY 5, 6 DESC

Wyraźnie widać, iż w celu pobrania informacji o historii zatrudnienia musieliśmy złączyć aż cztery tabele. W przeciwieństwie do poprzedniego wyniku, mamy tutaj nie jeden, a trzy rekordy. Spowodowane jest to faktem, iż w tabeli EmployeeDepartmentHistory występują trzy wiersze z ContactID równym 1037. To z kolei sprawia, że podczas złączenia EmployeeDepartmentHistory z Employee w tabeli wynikowej tworzone są trzy wpisy.
Przykłady, które przedstawiłem powyżej są złączeniami typu INNER JOIN (złączenie wewnętrzne). Tabela wynikowa tworzona jest poprzez sprawdzenie, czy w obu tabelach znajdują się pasujące do siebie dane.

@STRONA@

Innym typem złączenia jest CROSS JOIN. Tworzy on iloczyn kartezjański dwóch tabel. Rezultatem takiego złączenia jest przyporządkowanie każdemu wierszowi z pierwszej tabeli wszystkich rekordów z drugiej tabeli.

Należy używać powyższego złączenia z dużą uwagą. Nieprzemyślane użycie może doprowadzić do sytuacji gdzie serwer ulegnie zawieszeniu. Wystarczy w tym celu przeprowadzić małą symulację. Jeśli obie tabelki miałyby zaledwie po 10 000 rekordów tabela wynikowa złączenia typu CROSS JOIN miałaby 100 000 000!!

Kolejnym rodzajem złączeń są złączenia zewnętrzne. OUTER JOIN, bo o nich tutaj mowa, służą do ograniczenia w zbiorze wynikowym wierszy z jednej tablicy, podczas gdy wiersze z drugiej tablicy nie zostaną ograniczone.

LEFT OUTER JOIN Zawiera wszystkie wiersze z pierwszej tabeli i pasujące wiersze z rekordy z drugiej tabeli
RIGHT OUTER JOIN Zawiera wszystkie wiersze z drugiej tabeli i pasujące rekordy z pierwszej tabeli
FULL OUTER JOIN Zawiera wszystkie nie pasujące i pasujące wiersze z obydwóch tabel

Przypuśćmy, że chcemy uzyskać listę klientów wraz z numerem karty kredytowej. Jeśli zastosowalibyśmy zwykłe wewnętrzne złączenie, nie uzyskalibyśmy informacji o tych osobach, które nie posiadają karty kredytowej. Dzieje się tak z bardzo prostej przyczyny. Złączenie INNER JOIN zostałoby utworzone po kolumnie CreditCardID, a dla pewnego zbioru kontaktów nie istnieją wpisy w kolumnie ContactCreditCard. Jeśli zastosujemy złączenie zewnętrzne uzyskamy wszystkie wartości z tabeli kontaktów, a dla tych rekordów gdzie będziemy mieli wartość nieokreśloną otrzymamy w tabeli wynikowej NULL.

SELECT Person.Contact.FirstName, Person.Contact.LastName, ISNULL(Sales.CreditCard.CardNumber, 'BRAK') AS CardNumber
FROM   Sales.CreditCard LEFT OUTER JOIN
       Sales.ContactCreditCard ON Sales.CreditCard.CreditCardID = Sales.ContactCreditCard.CreditCardID RIGHT OUTER JOIN
       Person.Contact ON Sales.ContactCreditCard.ContactID = Person.Contact.ContactID
ORDER BY 3 DESC

Dodatkowego wyjaśnienia wymaga jeszcze tylko funkcja systemowa ISNULL. Umożliwia ona takie sformatowanie wyniku zapytania, że gdy w kolumnie pojawi się wartość NULL automatycznie zostanie pod nią podstawiona wartość podana jako drugi argument w funkcji. Jeśli w tym przypadku zastosowalibyśmy prawe złączenie zewnętrzne, moglibyśmy uzyskać tabelę z wszystkimi numerami kont i przyporządkowanymi im osobami. Z tą różnicą, że jeśli któryś z numerów karty kredytowej nie miałby przyporządkowanego kontaktu, miałby standardowo wpisaną wartość nieokreśloną.

Podsumowanie

Złączenia są chyba najważniejszym mechanizmem relacyjnych baz danych. Za ich pomocą możemy wybierać skorelowane ze sobą dane z różnych tabel, przeprowadzać operacje tworzenia raportów i wydruków.
Podstawą do efektywnego projektowania i użytkowania profesjonalnych serwerów bazodanowych jest poprawne zrozumienie procesu tworzenia złączeń. Jeśli na tym etapie będziemy mieli wątpliwości, powinniśmy jeszcze raz przemyśleć cały proces tworzenia złączeń i samemu poeksperymentować z bardziej skomplikowanymi zapytaniami.

Jak wykorzystać Copilot w codziennej pracy? Kurs w przedsprzedaży
Jak wykorzystać Copilot w codziennej pracy? Kurs w przedsprzedaży

Wydarzenia