Wszystkie zagadnienia: Bazy danych
zaawansowany SQLUNIONmatura rozszerzona z informatyki

UNION – operacje na zbiorach

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.

01

Opis

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.

  • Zgodność kolumn. Liczba i typy kolumn w obu zapytaniach muszą się zgadzać – w przeciwnym razie baza zgłasza błąd, zanim cokolwiek policzy.
  • Duplikaty tylko przy UNION. Sam UNION porównuje wiersze i usuwa powtórzenia; UNION ALL nie robi tego wcale, więc jest szybszy.
  • Jedno ORDER BY na koniec. Sortowanie obejmuje cały połączony wynik i pisze się je raz, po ostatnim SELECT.
  • Nazwy kolumn z pierwszego zapytania. Etykiety nadane w kolejnych członach są przy nazywaniu wyniku ignorowane.

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.

02

Zapytanie

-- 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
Wynik zapytania
grupailu
srednia co najmniej 4,03
srednia ponizej 4,02
-- 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)
Wynik zapytania
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)
Wynik zapytania
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
Wynik zapytania
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
Wynik zapytania
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
Wynik zapytania
błąd
Liczba kolumn w obu zapytaniach UNION musi być taka sama
03

Dialekt

W AccessieStandardowy SQLZnaczenie
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
04

Porównanie

CechaUNIONUNION ALL
Duplikatyusuwazachowuje
Szybkośćwolniejszy – porównuje i sortuje wiersze, żeby znaleźć powtórzeniaszybszy – nie porównuje wierszy między sobą
Kiedy używaćzbiory bez sensownych powtórzeń, np. rozłączne kategoriezestawienia, gdzie powtórzenie jest informacją, np. suma z dwóch źródeł
Kolejność wierszynieokreślona bez ORDER BYkolejność zapytań zachowana – najpierw pierwszy SELECT, potem drugi
05

Typowe błędy

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.

06

Zapamiętaj

  • UNION łączy WYNIKI zapytań „jeden pod drugim", a nie tabele obok siebie.
  • Liczba i typy kolumn w obu zapytaniach muszą się zgadzać.
  • Nazwy kolumn w wyniku bierze się z pierwszego zapytania.
  • UNION usuwa duplikaty; UNION ALL zachowuje wszystkie wiersze i jest szybszy.
  • ORDER BY pisze się raz, na samym końcu – dotyczy całego połączonego wyniku.
  • Różnicę i część wspólną zbiorów zapisuje się przez NOT IN / IN z podzapytaniem – standardowe EXCEPT/INTERSECT nie istnieją w Accessie.
  • Tekst wpisany wprost w SELECT, z etykietą AS, pozwala podpisać, z którego zapytania pochodzi wiersz wyniku.
07

Sprawdź się

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:

nazwiskogrupa
Kowalskaklasa 3A
Wiśniewskiklasa 3A
Kowalskaśrednia > 4,5
Wiśniewskiśrednia > 4,5

Uzupełnij odpowiedzi na podstawie wyniku powyżej:

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

08

Najczęściej zadawane pytania

Czy UNION łączy tabele, czy zapytania?

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.

Skąd biorą się nazwy kolumn w wyniku?

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.

Czy można połączyć więcej niż dwa zapytania?

Tak, kolejne UNION dopisuje się jedno po drugim. Wszystkie muszą spełniać te same warunki co do liczby i typów kolumn.

Czym UNION różni się od złączenia (JOIN)?

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.

Czy UNION zawsze jest wolniejszy niż UNION ALL?

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.

Jak zapisać różnicę zbiorów, skoro Access nie zna EXCEPT?

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.

Czy w każdym z zapytań łączonych przez UNION można użyć własnego WHERE albo GROUP BY?

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.

Co się stanie, jeśli jedna z kolumn ma wartość NULL?

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.

Przećwicz w zadaniach maturalnych

Do tego hasła nie mamy jeszcze przypisanych pojedynczych zadań. Cały spis zadań maturalnych z tego działu jest tutaj:

Zadania z działu: SQL

↗

Powiązane zagadnienia