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.
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.