JOIN

Polecenie JOIN służy do dołączania powiązanych pól przechowywanych w jednej lub kilku tabelach. Dołączasz drugą tabelę do pierwszej i określasz sposób połączenia danych. Poniżej podstawowy przykład:

SELECT * FROM Person.Address
LEFT JOIN Person.BusinessEntityAddress USING (AddressID)
W powyższym przykładzie dzieje się kilka istotnych rzeczy. Trzeba zacząć od polecenia LEFT JOIN, które oznacza, że chcesz dołączyć tabelę za pomocą łączenia lewego. Kolejnym krokiem jest wskazanie tabeli, którą chcesz dołączyć. Polecenie USING określa, że w obu tabelach powinna wystąpić kolumna służąca do połączenia obu tabel.

Nie musisz się jednak przejmować jeżeli kolumny mają różne nazwy w Twoich tabelach. Możesz użyć polecenia ON:

SELECT * FROM Person.Address
LEFT JOIN Person.BusinessEntityAddress ON Person.Address.AddressID = Person.BusinessEntityAddress.AddressID

Polecenie ON jawnie określa, które kolumny mają zostać użyte do połączenia. Istnieje wiele typów złączeń - w tym podrozdziale skupimy się na krótkim przedstawieniu ich właściwości:

  • (INNER) JOIN - zwraca rekordy z dopasowaniem w obu tabelach;
  • LEFT (OUTER) JOIN – zwraca wszystkie rekordy z lewej tabeli z dowolnymi dopasowaniami z prawej tabeli. Jeżeli nie ma dopasowań rekordy z lewej tabeli nadal są zwracane;
  • RIGHT (OUTER) JOIN – jest to przeciwieństwo powyższego przykładu: zwracane są wszystkie rekordy z prawej tabeli wraz z dowolnymi dopasowaniami z lewej;
  • FULL (OUTER) JOIN - zwraca rekordy z dopasowaniem z dowolnej tabeli.

Użycie składni INNER lub OUTER jest opcjonalne. Może to ułatwić zrozumienie ale nie jest wymagane przy każdym zapytaniu.

Przykład

W ramach przykładu dokonamy połączenia kilku tabel w których wykorzystamy również aliasy celem poprawy czytelności naszego zapytania.

Zanim jednak przejdziemy dalej spójrzmy na diagram bazy danych, tj. połączenia pomiędzy tabelami: AdventureWorks2019 - diagram

Wiem, zrzut ekranu nie jest zbyt czytelny. Otwórzcie go proszę w nowej zakładce swojej przeglądarki.

Jako, że nie jestem autorem tej bazy danych nie będę zakładał żadnego scenariusza - wykorzystując powyższy diagram dokonamy połączenia pomiędzy wieloma tabelami. Przykładowe zapytanie, które możemy wygenerować przedstawia się w poniższy sposób:

-- dobrą praktyką jest umieszczenie nazw kolumn w osobnych liniach
-- w razie błędu wiemy gdzie dokładnie szukać
-- ułatwiamy również interpretację naszego kodu
-- ALIASY: zwykle stosuję skrót od nazwy tabeli
-- Employee, alias: e
-- BusinessEntityAddress: bea
SELECT 
    e.BusinessEntityID,
	p.Title,
	p.FirstName,
    p.MiddleName,
    p.LastName,
    e.JobTitle, 
    pp.PhoneNumber,
    pnt.Name AS RodzajNumeruTelefonu,
    ea.EmailAddress as AdresEmail,
    p.EmailPromotion,
    a.AddressLine1,
    a.City,
    sp.Name AS NazwaProwincjiStanowej, -- baza stworzona dla danych w obrębie USA 
    a.PostalCode,
    cr.Name AS NazwaRegionyKraju, -- prawie jak województwo
    p.AdditionalContactInfo
FROM HumanResources.Employee e
    INNER JOIN Person.Person p
    ON p.BusinessEntityID = e.BusinessEntityID
    INNER JOIN Person.BusinessEntityAddress bea 
    ON bea.BusinessEntityID = e.BusinessEntityID 
    INNER JOIN Person.Address a 
    ON a.AddressID = bea.AddressID
    INNER JOIN Person.StateProvince sp 
    ON sp.StateProvinceID = a.StateProvinceID
    INNER JOIN Person.CountryRegion cr 
    ON cr.CountryRegionCode = sp.CountryRegionCode
    LEFT OUTER JOIN Person.PersonPhone pp
    ON pp.BusinessEntityID = p.BusinessEntityID
    LEFT OUTER JOIN Person.PhoneNumberType pnt
    ON pp.PhoneNumberTypeID = pnt.PhoneNumberTypeID
    LEFT OUTER JOIN Person.EmailAddress ea
    ON p.BusinessEntityID = ea.BusinessEntityID
WHERE pnt.PhoneNumberTypeID = 3 -- zwracamy tylko osoby z numerami bezpośrednio do biura

Podsumowanie

Dołączając do zespołu pracującego nad dużym projektem możecie spotkać się ze ścianą. Co dokładnie mam na myśli? Byliście w stanie przeanalizować powyższy diagram i dokładnie zrozumieć relacje pomiędzy tabelami? Ja też nie...

Oczywiście, będzie krótkie wprowadzenie mające na celu rozjaśnienie struktury bazy danych oraz relacji między tabelami. Parę dni później dostaniecie informację, że trzeba nieco poprawić zapytanie i dokleić dwie kolejne kolumny ponieważ brakuje ich w wynikach.

Przy 5 tabelach to nie będzie żaden problem, co jednak przy 50? Istnieje pewne zapytanie, które pozwoli nam spojrzeć z bliska na relacje pomiędzy różnymi tabelami oraz wyciągnąć informację skąd te dane pochodzą (nie jestem jego autorem, źródło w pierwszej linii zapytania):

-- https://dataedo.com/kb/query/sql-server/list-table-columns-with-their-foreign-keys
-- Zapytanie przygotowane dla SQL Server
select schema_name(tab.schema_id) + '.' + tab.name as [table],
    col.column_id,
    col.name as column_name,
    case when fk.object_id is not null then '>-' else null end as rel,
    schema_name(pk_tab.schema_id) + '.' + pk_tab.name as primary_table,
    pk_col.name as pk_column_name,
    fk_cols.constraint_column_id as no,
    fk.name as fk_constraint_name
from sys.tables tab
    inner join sys.columns col 
        on col.object_id = tab.object_id
    left outer join sys.foreign_key_columns fk_cols
        on fk_cols.parent_object_id = tab.object_id
        and fk_cols.parent_column_id = col.column_id
    left outer join sys.foreign_keys fk
        on fk.object_id = fk_cols.constraint_object_id
    left outer join sys.tables pk_tab
        on pk_tab.object_id = fk_cols.referenced_object_id
    left outer join sys.columns pk_col
        on pk_col.column_id = fk_cols.referenced_column_id
        and pk_col.object_id = fk_cols.referenced_object_id
order by schema_name(tab.schema_id) + '.' + tab.name,
    col.column_id

Na podstawie powyższego zapytania i poniższej części wyniku: AdventureWorks2019 - analiza ograniczeń możecie w prosty sposób wywnioskować połączenia pomiędzy kilkoma tabelami oraz wyciągnać informację dotyczące klucza głównego/obcego. Będzie to niezbędne w celu przygotowania poprawnego zapytania i połączenia odpowiednich tabel.