Gdy łączysz wyniki kilku zapytań SQL, wybór między UNION a UNION ALL wpływa nie tylko na liczbę rekordów, lecz także na czas wykonania całej operacji. Porównanie union vs union all pokazuje, kiedy usuwać powtórzenia, kiedy zachować każdy wiersz oraz jak uniknąć typowych błędów z typami danych, kolejnością kolumn i sortowaniem.
Najważniejsza decyzja zależy od tego, czy duplikaty są danymi, czy problemem
- UNION łączy wyniki i usuwa identyczne wiersze.
- UNION ALL zachowuje wszystkie rekordy, także powtórzenia.
- Obie części zapytania muszą zwracać taką samą liczbę kolumn w zgodnej kolejności.
- UNION ALL zwykle działa szybciej, bo nie wymaga deduplikacji.
- Jeśli powtórzenia mają znaczenie biznesowe, użycie zwykłego UNION może zafałszować wynik.
Jak działają UNION i UNION ALL
Oba operatory ustawiają wyniki zapytań jeden pod drugim. Nie dopasowują rekordów po wspólnym kluczu, więc działają inaczej niż JOIN, który łączy kolumny z powiązanych tabel w tym samym wierszu.
SELECT email
FROM klienci_2025
UNION
SELECT email
FROM klienci_2026;
W tym przykładzie otrzymasz jedną kolumnę z adresami e-mail z obu tabel. Jeśli ten sam adres występuje w obu źródłach, UNION pokaże go tylko raz. Operator wykonuje więc działanie podobne do połączenia zbiorów, w którym powtarzające się elementy są redukowane.
SELECT email
FROM klienci_2025
UNION ALL
SELECT email
FROM klienci_2026;
Wersja z dopiskiem ALL zachowa każdy rekord. Jeżeli adres pojawi się dwa razy w pierwszej tabeli i trzy razy w drugiej, wynik będzie zawierał pięć wierszy. To często właściwe zachowanie przy raportowaniu sprzedaży, analizie logów lub łączeniu tabel partycjonowanych według miesięcy.
| Cecha | UNION | UNION ALL |
|---|---|---|
| Duplikaty | Usuwa identyczne wiersze | Zachowuje wszystkie wiersze |
| Wydajność | Zwykle niższa przy dużych wynikach | Zwykle wyższa |
| Zastosowanie | Lista unikalnych wartości | Pełny zbiór danych i poprawne liczenie rekordów |
| Ryzyko | Może usunąć potrzebne rekordy | Może zwrócić powtórzenia, których nie zauważysz |
Warunki poprawnego łączenia zapytań
Każde zapytanie połączone operatorem zbiorów musi zwracać tyle samo kolumn. Kolumny na tych samych pozycjach powinny mieć zgodne albo możliwe do konwersji typy danych.
SELECT id, nazwa
FROM produkty
UNION ALL
SELECT kod, opis
FROM uslugi;
To zapytanie może być poprawne, jeśli id i kod są typami liczbowymi, a nazwa i opis tekstem. Nazwy kolumn w wyniku pochodzą zazwyczaj z pierwszego SELECT-a, dlatego dobrze nadać im czytelne aliasy właśnie w pierwszej części.
SELECT id AS element_id, nazwa AS element_nazwa
FROM produkty
UNION ALL
SELECT kod, opis
FROM uslugi;
Nie musisz pobierać tych samych nazw kolumn, ale musisz zachować ich sensowny układ. Gdy jedno źródło nie ma danej informacji, użyj wartości NULL albo stałej z odpowiednim typem.
SELECT id, nazwa, cena, 'produkt' AS typ
FROM produkty
UNION ALL
SELECT id, nazwa, NULL AS cena, 'usługa' AS typ
FROM uslugi;
Ten wzorzec jest bardzo praktyczny, bo oprócz wspólnych pól dodaje kolumnę identyfikującą źródło. Dzięki niej możesz później filtrować i grupować rekordy bez zgadywania, z której tabeli pochodzi dany wiersz.
Wydajność i deduplikacja w praktyce
Największa różnica wydajnościowa wynika z konieczności usunięcia duplikatów. Przy zwykłym UNION silnik bazy musi porównać wynik, często wykorzystując sortowanie albo strukturę haszującą. Przy UNION ALL rekordy mogą zostać przekazane dalej bez tego dodatkowego etapu.
Nie oznacza to, że UNION zawsze będzie powolny. Przy małych wynikach różnica może być niezauważalna, a optymalizator konkretnego systemu bazodanowego może wybrać różne strategie. Przy setkach tysięcy lub milionach wierszy koszt deduplikacji potrafi jednak stać się istotny.
Moja praktyczna zasada jest prosta. Najpierw ustalam, czy powtórzenia są błędem, a dopiero potem patrzę na szybkość. Jeśli dane pochodzą z rozłącznych partycji, na przykład tabel sprzedaz_styczen i sprzedaz_luty, wybieram UNION ALL, bo dodatkowe usuwanie duplikatów nie daje żadnej wartości.
Z kolei przy łączeniu list klientów z kilku niezależnych systemów zwykły UNION nie rozwiązuje problemu duplikatów biznesowych. Dwa rekordy tej samej osoby mogą różnić się nazwą, formatem telefonu albo adresem, więc dla SQL nie będą identyczne. W takiej sytuacji potrzebujesz deduplikacji opartej na kluczu, na przykład przez ROW_NUMBER() lub agregację.
SELECT email
FROM (
SELECT email FROM system_crm
UNION ALL
SELECT email FROM system_sprzedaz
) AS zrodla
GROUP BY email;
Takie podejście zachowuje kontrolę nad regułą unikalności. Możesz zdecydować, że identyfikatorem jest e-mail, numer klienta albo kombinacja kilku pól, zamiast zdawać się na porównanie całych wierszy.
Przykłady zastosowań, które dobrze pokazują różnicę
Lista unikalnych kategorii
SELECT kategoria
FROM produkty_online
UNION
SELECT kategoria
FROM produkty_sklep;
Jeżeli chcesz wyświetlić każdą kategorię tylko raz, UNION pasuje do celu. Nie ma znaczenia, ile produktów należy do kategorii „Laptopy”, ponieważ interesuje Cię sama lista kategorii.
Łączenie zdarzeń z wielu tabel
SELECT user_id, event_time, 'logowanie' AS event_type
FROM logowania
UNION ALL
SELECT user_id, event_time, 'zakup' AS event_type
FROM zakupy;
W raporcie aktywności użytkownika każdy rekord jest osobnym zdarzeniem. Usunięcie dwóch identycznych wierszy mogłoby zaniżyć liczbę logowań albo zakupów, dlatego UNION ALL jest tutaj bezpieczniejszym wyborem.
Łączenie danych bieżących i archiwalnych
SELECT id, kwota, data_sprzedazy
FROM sprzedaz_biezaca
UNION ALL
SELECT id, kwota, data_sprzedazy
FROM sprzedaz_archiwum;
Ten schemat spotykam często w hurtowniach danych. Jeżeli zakresy dat się nie nakładają, rekordy nie powinny się powtarzać, a zastosowanie UNION ALL ogranicza niepotrzebną pracę silnika.
Porównanie dwóch zbiorów bez łączenia kolumn
Operatory te bywają mylone z JOIN-em, ale odpowiadają na inne pytanie. UNION tworzy wspólną listę rekordów z kilku zapytań, natomiast JOIN pozwala zestawić na przykład klienta z jego zamówieniami na podstawie relacji między tabelami.
Jeżeli chcesz ustawić dane „jeden wynik pod drugim”, myśl o UNION. Jeżeli chcesz dodać informacje z drugiej tabeli do istniejącego wiersza, prawdopodobnie potrzebujesz JOIN-a.
Typowe błędy przy wyborze operatora
Użycie UNION do zwykłego sklejania danych
To najczęstszy przypadek. Programista chce połączyć dwa źródła, ale wybiera UNION, bo składnia wygląda znajomo. Skutek to ciche usunięcie powtórzeń, które może zmienić sumy, liczniki i wyniki raportów.
Założenie, że UNION usuwa każdy podobny rekord
SQL usuwa tylko wiersze identyczne w zakresie wszystkich zwracanych kolumn. Rekordy o tym samym id, ale innej dacie, cenie lub wartości typ pozostaną osobno. Gdy potrzebujesz unikalności według jednego pola, zapisz tę regułę jawnie.
Sortowanie każdej części zapytania
W większości dialektów ORDER BY umieszcza się na końcu całego wyrażenia, a nie po każdym SELECT-cie. Sortowanie poszczególnych części zwykle nie ma sensu, bo operator dopiero tworzy końcowy zbiór wynikowy.
SELECT id, nazwa
FROM produkty
UNION ALL
SELECT id, nazwa
FROM archiwum
ORDER BY nazwa;
Jeśli chcesz sortować lub ograniczać osobne zapytania, potrzebujesz nawiasów, podzapytań albo składni zależnej od używanego silnika. Szczegóły mogą różnić się między PostgreSQL, MySQL, SQL Server i Oracle, dlatego przy bardziej złożonych konstrukcjach sprawdzam plan wykonania oraz dokumentację konkretnej bazy.
Przeczytaj również: Bazy danych od podstaw - SQL, Python i dobre praktyki
Brak jawnej kontroli typów
Łączenie liczby z tekstem albo daty z niejednoznacznym ciągiem znaków może zakończyć się błędem lub nieoczekiwaną konwersją. Jawne CAST-y zwiększają czytelność i pomagają uniknąć sytuacji, w której wynik zależy od ustawień sesji lub wersji silnika.
Jak podjąć właściwą decyzję w jednym kroku
Zadaj sobie jedno pytanie: czy identyczny rekord powinien pojawić się w wyniku więcej niż raz? Jeśli odpowiedź brzmi „tak”, użyj UNION ALL. Jeśli „nie”, wybierz UNION, ale upewnij się, że identyczność całego wiersza odpowiada rzeczywistej regule biznesowej.
- Wybierz UNION, gdy tworzysz unikalną listę wartości albo świadomie chcesz usunąć powtórzenia.
- Wybierz UNION ALL, gdy liczysz zdarzenia, łączysz partycje lub budujesz pełny zbiór danych.
- Dodaj kolumnę źródłową, gdy rekordy pochodzą z kilku tabel i później będziesz je analizować.
- Przy dużych zbiorach sprawdź plan wykonania i nie dodawaj deduplikacji bez konkretnej potrzeby.
W codziennej pracy częściej zaczynam od UNION ALL, ponieważ zachowuje dane bez ukrytej utraty rekordów. Dopiero gdy wymagania mówią o unikalności, przechodzę na UNION albo stosuję precyzyjniejszą deduplikację po wybranym kluczu.
Najlepszy operator zależy od znaczenia rekordu
Różnica między tymi konstrukcjami nie sprowadza się do tego, że jedna jest „szybsza”, a druga „dokładniejsza”. UNION zmienia wynik przez usunięcie duplikatów, natomiast UNION ALL zachowuje pełną informację o liczbie wystąpień.
Dlatego przed optymalizacją zapytania sprawdzam, co reprezentuje pojedynczy wiersz i czy jego powtórzenie ma znaczenie. Ta krótka analiza zwykle prowadzi do poprawnej decyzji szybciej niż mechaniczne kierowanie się samą wydajnością.
