
Strona o szybkim czytaniu.
Dane, które chcemy uzyskać z bazy danych zazwyczaj znajdują się w więcej niż jednej tabeli bazodanowej. W takim przypadku nasze zapytanie musi się odpowiednio odwołać do więcej niż jednej tabeli. Mechanizm ten nazywa się w SQL złączeniami.
Aby zapytanie czerpało dane z kilku tabel, należy po klauzuli FROM zamiast jednej tebeli wymienić wszystkie wymagane, rozdzielając je przecinkami.
SELECT klt_adres, zam_opis
FROM klienci, zamowienia;
Zapytanie takie zwróci iloczyn kartezjański wierszy z obu tabel.
Złączenia kartezjańskie stosuje się bardzo rzadko, gdyż produkują bardzo dużą ilość wierszy, które nie zawierają logicznie spójnych informacji.
Jeżeli w zapytaniu chcemy odwołać się do kolumny, która występuje w kilku tabelach, to, aby uniknąć niejednoznaczności, odwołanie to musi zawierać nazwę kolumny poprzedzoną nazwą tabeli.
SELECT klienci.klt_adres,
zamowienia.zam_opis
FROM klienci, zamowienia;
Stosowanie pełnej nazwy kolumn (wraz z nazwą tabeli) w przypadku pisania zapytań do kilku tabel należy do dobrego zwyczaju, nawet gdy nie jest to konieczne ze względu na niejednoznaczność nazw. Zapytanie staje się dzięki temu czytelniejsze.
Złączenia kartezjańskie stosuje się niezwykle rzadko. Najczęściej, kiedy zadajemy zapytanie do dwu tabel, zależy nam na uzyskaniu tylko takich kombinacji wierszy z obu tabel, które sobie w jakiś sposób odpowiadają, np. chcemy klientów i tylko zamówienia przez nich złożone.
Złączenia, które zwracają właśnie taki zbiór wierszy z obu tabel, które sobie w jakimś stopniu odpowiadają nazywamy złączeniami wewnętrznym (inner join).
Aby zrealizować złączenie wewnętrzne w Oracle SQL należy w klauzuli WHERE
dodać tzw. warunek złączenia, czyli warunek, w którym określamy jakie wiersze z obu tabel odpowiadają sobie nawzajem.
SELECT klienci.klt_nazwisko, klienci.klt_nazwa, zamowienia.zam_opis
FROM klienci, zamowienia
WHERE klienci.klt_id = zamowienia.zam_klt_id;
Zamiast odwoływać się do kolumn poprzez pełne nazwy tabel można tabelom, podobnie jak kolumnom, nadać aliasy i odwoływać się do kolumn poprzez te aliasy.
SELECT K.klt_email,
Z.zam_opis,
Z.zam_data_zamowienia
FROM klienci K, zamowienia Z
WHERE K.klt_id = Z.zam_klt_id;
Jeżeli nadamy tabeli alias, to nie można się już będzie do niej odwoływać przez nazwę.
W złączeniu mogą brać udział więcej niż 2 tabele. W takim przypadku, aby uniknąć złączenia kartezjańskiego należy wyspecyfikować więcej warunków łączących tabele.
SELECT Z.zam_opis, P.pzm_ilosc, T.tow_nazwa
FROM zamowienia Z, pozycje_zamowien P, towary T
WHERE Z.zam_id = P.pzm_zam_id
AND T.tow_id = P.pzm_tow_id
ORDER BY Z.zam_opis;
W przypadku łączenia N tabel powinniśmy wyspecyfikować N-1 złączeń pomiędzy tabelami.
W przypadku zapytań odnoszących się do wielu tabel, oprócz warunku złączenia w klauzuli WHERE
mogą pojawić się także inne warunki wpływające na selekcję wierszy.
SELECT DISTINCT T.tow_nazwa
FROM zamowienia Z, pozycje_zamowien P, towary T
WHERE Z.zam_id = P.pzm_zam_id
AND T.tow_id = P.pzm_tow_id
AND Z.zam_data_realizacji IS NULL;
We wszystkich złączeniach można również dokonywać agregacji i grupowań.
SELECT K.klt_imie, K.klt_nazwisko, count(*)
FROM zamowienia Z, klienci K
WHERE K.klt_id = Z.zam_klt_id
AND K.klt_typ = 'IND'
GROUP BY K.klt_imie, K.klt_nazwisko;
W złączeniach można również korzystać z klauzuli HAVING
do wprowadzania warunków zawierających funkcje grupowe.
SELECT Z.zam_opis
FROM zamowienia Z, pozycje_zamowien P
WHERE Z.zam_id = P.pzm_zam_id
GROUP BY Z.zam_opis
HAVING count(*) > 1;
Warunek definiujący złączenie nie koniecznie musi się składać z jednego porównania. Może to być również warunek złożony z operatorów większości, zakresu, itp.
SELECT T.tow_nazwa, T.tow_cena, G.grc_rabat
FROM towary T, grupy_cen G
WHERE T.tow_cena*20 BETWEEN G.grc_cena_od AND G.grc_cena_do;
W przypadku złączeń wewnętrznych, jeżeli w jednej z tabel istnieje rekord, który nie ma żadnego odpowiednika w drugiej tabeli, to rekord ten jest pomijany w końcowym wyniku.
SELECT K.klt_nazwisko,
Z.zam_opis
FROM klienci K, zamowienia Z
WHERE K.klt_id = Z.zam_klt_id
AND K.klt_typ = 'IND';
Czasami taka sytuacja nas nie zadawala. W takim wypadku możemy wyróżnić jedną z tabel i wykonać tzw. złączenie zewnętrzne (outer join), aby otrzymać z wyróżnionej tabeli wszystkie rekordy, nawet te, które nie mają swojego odpowiednika w drugiej z tabel.
Aby wykonać złączenie zewnętrzne musimy skorzystać z operatora złączenia zewnętrznego (+)
. Umieszczamy go w warunku złączenia po nazwie kolumny z tabeli która jest „uboższa”, czyli tam, gdzie „brakuje” rekordów.
SELECT K.klt_nazwisko,
Z.zam_opis
FROM klienci K, zamowienia Z
WHERE K.klt_id = Z.zam_klt_id(+)
AND K.klt_typ = 'IND';
Nie jest obojętne przy której kolumnie postawimy operator złączenia zewnętrznego. W przypadku zamiany miejsc, zapytanie nabierze zupełnie innego kształtu.
Czasami zachodzi potrzeba odwołania się w jednym zapytaniu dwukrotnie do tej samej tabeli.
SELECT SK.tow_nazwa,
T.tow_nazwa
FROM towary SK, towary T
WHERE SK.tow_tow_id = T.tow_id;
W przypadku samozłączeń tę samą tebelę traktuje się jako dwie niezależne tabele. W związku z tym można traktować takie złączenie jak każde inne, stosując dowolne poznane konstrukcje.
SELECT T.tow_nazwa,
T.tow_cena,
sum(SK.tow_cena)
FROM towary SK, towary T
WHERE SK.tow_tow_id = T.tow_id
GROUP BY T.tow_nazwa, T.tow_cena
HAVING T.tow_cena < sum(SK.tow_cena);
Operatory zbiorowe służą do odpowiedniego łączenia kilku zbiorów wierszy wygenerowanych przez niezależne zapytania.
Operatory zbiorowe stosuje się w następującej składni:
SELECT ...
operator_zbiorowy
SELECT ...
W Oracle SQL istnieją cztery rodzaje operatorów zbiorowych:
UNION ALL
Operator ten powoduje, że w wyniku zapytania otrzymujemy sumę wszystkich wierszy, które znajdowały się w obu zapytaniach składowych.
SELECT klt_imie, klt_nazwisko
FROM klienci
WHERE klt_typ = 'IND'
UNION ALL
SELECT tow_nazwa, to_char(tow_cena)
FROM towary
WHERE tow_podatek = 7;
UNION
Operator ten działa podobnie jak UNION ALL
, ale z wyniku końcowego eliminowane są wiersze, które powtarzają się w obu zbiorach składowych.
SELECT *
FROM zamowienia
WHERE to_char(zam_data_zamowienia, 'yyyy') = '2000'
UNION
SELECT *
FROM zamowienia
WHERE zam_data_realizacji IS NULL;
INTERSECT
Operator ten powoduje, że w ostatecznym zbiorze wynikowym znajdują się tylko wiersze, które znajdowały się jednocześnie w obu zbiorach składowych (zbiór wspólny).
SELECT tow_nazwa
FROM towary
INTERSECT
SELECT klt_nazwisko
FROM klienci;
MINUS
Operator ten powoduje wyselekcjonowanie wierszy, które znajdują się w zbiorze wierszy zwróconym przez pierwsze zapytanie, a nie znajdują się w zbiorze wierszy zwróconym przez drugie zapytanie.
SELECT *
FROM klienci
MINUS
SELECT K.*
FROM klienci K, zamowienia Z
WHERE K.klt_id = Z.zam_klt_id
AND Z.zam_data_realizacji IS NOT NULL;
Różnymi operatorami zbiorowymi można jednocześnie traktować więcej niż dwa zapytania.
SELECT *
FROM klienci
WHERE klt_typ = 'GSP'
UNION
(SELECT *
FROM klienci
MINUS
SELECT K.*
FROM klienci K, zamowienia Z
WHERE K.klt_id = Z.zam_klt_id);
W przypadku stosowania operatorów zbiorowych należy pamiętać o następujących rzeczach: