UNION łączy wyniki dwóch zapytań SQL w jeden zbiór i usuwa duplikaty (UNION ALL je zachowuje) – zastępuje brakujące w Accessie EXCEPT i INTERSECT.
Złączenie tabel dokłada kolumny – wynik staje się szerszy. UNION działa w drugą stronę: skleja wyniki dwóch zapytań jeden pod drugim, więc wynik staje się dłuższy. To odpowiednik sumy zbiorów.
Dwa warunki, które muszą być spełnione. Oba łączone zapytania muszą zwracać tę samą liczbę kolumn, a odpowiadające sobie kolumny muszą być tego samego typu. Nazwy kolumn nie muszą się zgadzać – nagłówki wyniku bierze się z pierwszego zapytania.
UNION usuwa duplikaty, tak jak DISTINCT: wiersz występujący w obu zapytaniach pojawi się w wyniku raz. Jeśli powtórzenia mają zostać, służy do tego wariant UNION ALL – dodatkowo szybszy, bo baza nie musi porównywać wierszy między sobą.
Wpisany wprost tekst jako kolumna. Przy UNION bardzo przydaje się możliwość umieszczenia w SELECT napisu, którego nie ma w żadnej tabeli. Dzięki temu można podpisać, z którego zapytania pochodzi dana część wyniku – bez tego dwie liczby jedna pod drugą są nie do odróżnienia. Taki napis, jak każde wyrażenie, wymaga etykiety AS.
Pułapka z sortowaniem. ORDER BY w zapytaniu z UNION odnosi się do całego połączonego wyniku, a nie do poszczególnych części. Próba posortowania osobno każdego członu zwykle nie działa tak, jak się tego oczekuje – i, co gorsza, nie zgłasza błędu. Objawia się to brakiem sortowania tam, gdzie się go spodziewaliśmy. Rozwiązaniem jest ujęcie każdego członu w nawiasy albo przeniesienie go do podzapytania w miejsce tabeli.
Różnica i część wspólna zbiorów. Nie każda baza ma na nie osobne słowa kluczowe, ale obie da się zapisać podzapytaniem:
Różnica – „co jest w pierwszej tabeli, a czego nie ma w drugiej" – to NOT IN z podzapytaniem. Tak odpowiada się na pytanie „którzy uczniowie nie mają ani jednej oceny".
Część wspólna – „co występuje w obu" – to IN z podzapytaniem. Warunek jest ten sam, tylko bez zaprzeczenia.
W obu przypadkach kluczowe jest, żeby kolumna porównywana w WHERE i kolumna zwracana przez podzapytanie opisywały to samo – zwykle jest to klucz łączący obie tabele.
UNION porównuje wiersze i usuwa powtórzenia; UNION ALL nie robi tego wcale, więc jest szybszy.SELECT.Wariant z LEFT JOIN i warunkiem IS NULL to ta sama technika, co antyzłączenie opisane przy złączeniach wielotabelowych, a zapytanie w nawiasie po IN/NOT IN to zwykłe podzapytanie.
-- Wpisany wprost tekst podpisuje, skąd pochodzi która liczba SELECT 'srednia co najmniej 4,0' AS grupa, COUNT(*) AS ilu FROM uczniowie WHERE srednia >= 4.00 UNION SELECT 'srednia ponizej 4,0' AS grupa, COUNT(*) AS ilu FROM uczniowie WHERE srednia < 4.00
| grupa | ilu |
|---|---|
| srednia co najmniej 4,0 | 3 |
| srednia ponizej 4,0 | 2 |
-- Różnica zbiorów: uczniowie, którzy nie mają ani jednej oceny SELECT nazwisko FROM uczniowie WHERE id NOT IN (SELECT uczen_id FROM oceny)
| nazwisko |
|---|
| Zając |
| Dąbrowski |
-- Część wspólna: uczniowie, którzy mają przynajmniej jedną ocenę SELECT nazwisko FROM uczniowie WHERE id IN (SELECT uczen_id FROM oceny)
| nazwisko |
|---|
| Kowalska |
| Nowak |
| Wiśniewski |
-- Dwie listy: klasa 3A oraz uczniowie ze średnią powyżej 4,5 SELECT nazwisko FROM uczniowie WHERE klasa = '3A' UNION SELECT nazwisko FROM uczniowie WHERE srednia > 4.5
| nazwisko |
|---|
| Kowalska |
| Wiśniewski |
-- To samo zestawienie bez usuwania powtórzeń, posortowane na koniec SELECT nazwisko FROM uczniowie WHERE klasa = '3A' UNION ALL SELECT nazwisko FROM uczniowie WHERE srednia > 4.5 ORDER BY nazwisko
| nazwisko |
|---|
| Kowalska |
| Kowalska |
| Wiśniewski |
| Wiśniewski |
-- Pułapka: drugie zapytanie ma inną liczbę kolumn SELECT nazwisko, srednia FROM uczniowie WHERE klasa = '3A' UNION SELECT nazwisko FROM uczniowie WHERE srednia > 4.5
| błąd |
|---|
| Liczba kolumn w obu zapytaniach UNION musi być taka sama |
| W Accessie | Standardowy SQL | Znaczenie |
|---|---|---|
zapis w Accessie |
zapis standardowy |
znaczenie |
NOT IN (podzapytanie) lub LEFT JOIN ... WHERE ... IS NULL |
EXCEPT |
różnica zbiorów – wiersze z pierwszego zapytania, których nie ma w drugim |
IN (podzapytanie) |
INTERSECT |
część wspólna zbiorów – wiersze występujące w obu zapytaniach |
| Cecha | UNION | UNION ALL |
|---|---|---|
| Duplikaty | usuwa | zachowuje |
| Szybkość | wolniejszy – porównuje i sortuje wiersze, żeby znaleźć powtórzenia | szybszy – nie porównuje wierszy między sobą |
| Kiedy używać | zbiory bez sensownych powtórzeń, np. rozłączne kategorie | zestawienia, gdzie powtórzenie jest informacją, np. suma z dwóch źródeł |
| Kolejność wierszy | nieokreślona bez ORDER BY | kolejność zapytań zachowana – najpierw pierwszy SELECT, potem drugi |
Różna liczba kolumn w członach. SELECT nazwisko, srednia FROM uczniowie UNION SELECT nazwisko FROM uczniowie WHERE klasa = '3A' kończy się błędem jeszcze przed wykonaniem – baza od razu zgłasza niezgodność liczby kolumn. Naprawa polega na dopisaniu lub usunięciu kolumny tak, by oba zapytania zwracały tyle samo pól.
Niezgodne typy kolumn. SELECT nazwisko, srednia FROM uczniowie UNION SELECT nazwisko, klasa FROM uczniowie łączy w jednej pozycji liczbę (srednia) z tekstem (klasa). Access próbuje to przekonwertować i albo zgłasza błąd typu, albo – co gorsza – po cichu zamienia liczby na tekst, przez co dalsze sortowanie i porównania przestają działać tak jak powinny.
ORDER BY przy każdym SELECT z osobna. SELECT nazwisko FROM uczniowie WHERE klasa = '3A' ORDER BY nazwisko UNION SELECT nazwisko FROM uczniowie WHERE srednia > 4.5 ORDER BY nazwisko jest niepoprawne składniowo – ORDER BY wolno użyć tylko raz, na samym końcu całego zapytania, po ostatnim członie.
UNION zamiast UNION ALL przy liczeniu. Gdy dwa niezależne zliczenia przypadkiem dają tę samą liczbę, zwykły UNION scali je w jeden wiersz i zestawienie będzie miało za mało pozycji. Jeśli powtórzenie jest informacją, a nie błędem, właściwym wyborem jest UNION ALL.
Liczenie na to, że UNION usunie „te same" nazwiska. UNION porównuje CAŁE wiersze, nie pojedynczą kolumnę. Jeśli do obu członów dopiszesz etykietę grupy ('klasa 3A', 'średnia > 4,5'), to samo nazwisko z dwiema różnymi etykietami tworzy dwa RÓŻNE wiersze i żaden z nich nie zniknie. Duplikaty znikają dopiero wtedy, gdy w obu zapytaniach zgadzają się wszystkie kolumny – na przykład gdy oba zwracają samo nazwisko.
Zapomniany alias przy tekście wpisanym wprost. SELECT 'grupa A', COUNT(*) FROM uczniowie WHERE klasa = '3A' UNION SELECT 'grupa B', COUNT(*) FROM uczniowie WHERE klasa = '3B' działa, ale nagłówek pierwszej kolumny wychodzi bez sensownej nazwy. Brakującą etykietę dopisuje się przez AS, tak jak przy każdym innym wyrażeniu w SELECT.
Zakładanie stałej kolejności wierszy bez ORDER BY. Bez jawnego sortowania kolejność wierszy w wyniku UNION nie jest niczym gwarantowana – nawet jeśli w praktyce akurat wychodzi „po kolei", to przypadek silnika, a nie reguła języka. Gdy kolejność ma znaczenie dla zadania, trzeba ją wymusić przez ORDER BY na końcu.
Wynik zapytania SELECT nazwisko, 'klasa 3A' AS grupa FROM uczniowie WHERE klasa = '3A' UNION ALL SELECT nazwisko, 'średnia > 4,5' AS grupa FROM uczniowie WHERE srednia > 4.5:
| nazwisko | grupa |
|---|---|
| Kowalska | klasa 3A |
| Wiśniewski | klasa 3A |
| Kowalska | średnia > 4,5 |
| Wiśniewski | średnia > 4,5 |
Uzupełnij odpowiedzi na podstawie wyniku powyżej:
| Pytanie | Odpowiedź |
|---|---|
| Ile wierszy zwróciło to zapytanie z UNION ALL? | |
| Ile wierszy zostałoby, gdyby w tym zapytaniu UNION ALL zamienić na zwykły UNION? | |
| A ile, gdyby oba zapytania zwracały samo nazwisko, bez kolumny grupa, i połączył je UNION? | |
| Z którego zapytania (pierwszego czy drugiego) pochodzi nazwa kolumny "grupa"? | |
| Jakie słowo kluczowe usuwa duplikaty przy łączeniu dwóch zapytań? | |
| Ile razy w wyniku powtarza się nazwisko Kowalska? | |
| Podaj sam operator porównania z warunku, który dał wiersze "średnia > 4,5" | |
| Jaki fragment SQL trzeba dopisać na końcu, żeby posortować cały wynik po nazwisku? | |
| Ile kolumn musi mieć drugie zapytanie w UNION, jeśli pierwsze zwraca 2 kolumny? |
Gdyby drugie zapytanie zwracało kolumny zamiast dwóch, baza zgłosiłaby błąd jeszcze przed policzeniem wyniku, bo UNION wymaga liczby kolumn w obu zapytaniach.
Zapytania. To ważne rozróżnienie: łączone człony mogą pochodzić z tej samej tabeli, różniąc się tylko warunkiem – i właśnie tak najczęściej wygląda zestawienie zbiorcze.
Z pierwszego zapytania. Etykiety nadane w drugim i kolejnych członach są ignorowane, więc nie ma sensu ich powtarzać – choć nie zaszkodzi, jeśli poprawiają czytelność zapisu.
Tak, kolejne UNION dopisuje się jedno po drugim. Wszystkie muszą spełniać te same warunki co do liczby i typów kolumn.
Złączenie rozszerza wynik o kolumny z drugiej tabeli, więc wiersz robi się szerszy. UNION dokleja kolejne wiersze pod spodem, więc wynik robi się dłuższy, a liczba kolumn się nie zmienia.
Zwykle tak, bo musi porównać wszystkie wiersze wyniku, żeby znaleźć i usunąć duplikaty. Różnica rośnie wraz z liczbą wierszy. Gdy z góry wiadomo, że powtórzeń nie ma albo nie przeszkadzają, UNION ALL jest szybszym wyborem.
Przez NOT IN z podzapytaniem albo przez LEFT JOIN z warunkiem IS NULL na kluczu drugiej tabeli. Oba sposoby dają ten sam wynik – wiersze z pierwszego zapytania, których nie ma w drugim.
Tak, każdy człon jest samodzielnym zapytaniem – może mieć własny WHERE, GROUP BY i HAVING. Ograniczenie dotyczy tylko ORDER BY, które wolno napisać jedynie raz, na końcu całości.
NULL jest traktowany tak samo jak w innych porównaniach wierszy – dwa wiersze z NULL w tej samej kolumnie są uznawane za duplikat i przy zwykłym UNION zostaną zredukowane do jednego.
Do tego hasła nie mamy jeszcze przypisanych pojedynczych zadań. Cały spis zadań maturalnych z tego działu jest tutaj:
Zagadnienia z tego samego obszaru matury – warto je powtórzyć razem: