LEFT JOIN w SQL: zachowaj rekordy i uniknij błędów

Radosław Krajewski • 29 października 2026
Wynik zapytania LEFT JOIN: dane z Tabeli A z cenami z Tabeli B, gdzie brakujące ceny to NULL.

Spis treści

Raport ma pokazać wszystkich klientów, także tych bez zamówień. LEFT JOIN zwykle zachowa ich w wyniku, ale liczba wierszy może wzrosnąć, jeśli jeden klient ma kilka pasujących zamówień.

Diagram ilustrujący ORACLE SQL LEFT OUTER JOIN: zawiera wszystkie wiersze z Tabeli A i pasujące z Tabeli B.

LEFT JOIN zachowuje lewą tabelę, ale nie gwarantuje jednego wiersza na rekord

  • Lewa tabela dostarcza wszystkie swoje wiersze; brak dopasowania daje wartości NULL po prawej stronie.
  • Warunek w ON ogranicza dopasowania, a filtr w WHERE może usunąć wiersze bez dopasowania.
  • Wiele dopasowań po prawej stronie powiela wiersz z lewej tabeli.
  • Braki danych znajdziesz przez LEFT JOIN i test klucza po prawej stronie na NULL.
  • Składnia zależy od silnika: PostgreSQL obsługuje USING, a w SQL Server używa się ON.

Wynik zachowuje klientów, ale może zawierać ich kilka razy

Wyobraź sobie tabelę klientów i tabelę zamówień. Gdy klient nie ma zamówienia, jego dane nadal pojawią się w wyniku, a kolumny zamówienia będą miały wartość NULL. Tak działa zewnętrzne złączenie lewostronne, zapisywane też jako LEFT OUTER JOIN.

SELECT c.CustomerId, c.Name, o.OrderId
FROM Customers AS c
LEFT JOIN Orders AS o
    ON o.CustomerId = c.CustomerId;

Customers jest tu lewą tabelą, bo występuje przed operatorem. Jeśli dla klienta istnieją trzy zamówienia, zapytanie zwróci trzy wiersze z jego danymi. Uważam, że najważniejsza zasada brzmi: ten operator zachowuje lewą stronę, ale nie obiecuje jednego wiersza na każdy jej rekord.

To rozróżnienie przydaje się szczególnie wtedy, gdy raport ma pokazywać po jednym wierszu na klienta. Sam operator tego nie zapewni. Najpierw trzeba ustalić, czy wynik ma zawierać każde zamówienie, czy na przykład tylko łączną ich liczbę.

Warunek w ON zachowuje rekord bez pasującej płatności

Klient może mieć zamówienia, ale nie mieć żadnego opłaconego. Jeśli interesują Cię wszyscy klienci wraz z opłaconymi zamówieniami, filtr statusu umieść w ON:

SELECT c.CustomerId, c.Name, o.OrderId
FROM Customers AS c
LEFT JOIN Orders AS o
    ON o.CustomerId = c.CustomerId
   AND o.Status = 'Paid';

Klient bez opłaconych zamówień zostanie w wyniku, a kolumny zamówienia będą puste. Przeniesienie warunku statusu do WHERE odsieje wiersze, w których o.Status ma wartość NULL. W praktyce taki filtr często sprawia, że rezultat zachowuje się jak po INNER JOIN.

Sytuacja Prawdopodobny skutek Co zrobić
Potrzebujesz wszystkich klientów i tylko opłaconych zamówień Warunek statusu w WHERE usunie klientów bez opłaconego zamówienia Dodaj warunek statusu do ON
Potrzebujesz wyłącznie klientów z opłaconym zamówieniem Wynik ma pomijać klientów bez dopasowania Użyj INNER JOIN albo jawnego filtra w WHERE
Chcesz znaleźć klientów bez żadnego zamówienia Brak dopasowania oznacza NULL w kolumnach tabeli zamówień Testuj klucz zamówienia na IS NULL

Krótka reguła pomaga uniknąć przypadkowego odfiltrowania danych: ograniczasz to, co może się dopasować - umieść warunek w ON; ograniczasz gotowy wynik - użyj WHERE. Przy złączeniach zewnętrznych te miejsca nie są zamienne.

Trzy pasujące zamówienia zmienią trzy rekordy klientów w pięć wierszy

Załóżmy, że w tabeli są trzej klienci: pierwszy ma trzy zamówienia, drugi nie ma żadnego, a trzeci ma jedno. Po złączeniu otrzymasz pięć wierszy: trzy dla pierwszego klienta, jeden z pustymi danymi zamówienia dla drugiego i jeden dla trzeciego.

To częsty powód zawyżonych wyników w raporcie. Jeśli potem policzysz wiersze przez COUNT(*), klient bez zamówień dostanie wynik równy 1, bo jego zachowany wiersz nadal istnieje. Do policzenia zamówień użyj COUNT(o.OrderId), zakładając, że OrderId nie dopuszcza NULL.

SELECT c.CustomerId,
       c.Name,
       COUNT(o.OrderId) AS OrderCount
FROM Customers AS c
LEFT JOIN Orders AS o
    ON o.CustomerId = c.CustomerId
GROUP BY c.CustomerId, c.Name;

Nie naprawiaj takiego wyniku odruchowym DISTINCT. Może ukryć powtórzone wartości w wyświetlanych kolumnach, ale nie odpowie, które zamówienie ma zostać wybrane ani jak policzyć wiele dopasowań. Jeśli ma zostać jedno zamówienie, określ regułę wyboru, na przykład najnowszą datę, i zastosuj ją jawnie.

Brak dopasowania wykryjesz przez klucz po prawej stronie

To złączenie przydaje się także do wyszukiwania brakujących powiązań, na przykład klientów bez zamówień. Po połączeniu odfiltruj wiersze, w których klucz zamówienia jest pusty:

SELECT c.CustomerId, c.Name
FROM Customers AS c
LEFT JOIN Orders AS o
    ON o.CustomerId = c.CustomerId
WHERE o.OrderId IS NULL;

Testuj kolumnę będącą kluczem głównym lub innym polem NOT NULL. Gdy sprawdzisz pole, które samo może zawierać NULL, nie odróżnisz braku dopasowanego rekordu od dopasowanego rekordu z pustą wartością w tej kolumnie.

Alternatywą jest NOT EXISTS, który wyraża wprost, że szukasz klientów bez zamówienia:

SELECT c.CustomerId, c.Name
FROM Customers AS c
WHERE NOT EXISTS (
    SELECT 1
    FROM Orders AS o
    WHERE o.CustomerId = c.CustomerId
);

Przy prostym sprawdzaniu braków czytelność często rozstrzyga wybór. Dokumentacja MySQL opisuje wariant z LEFT JOIN i testem NULL, a w aplikacji możesz postawić na NOT EXISTS, jeśli wyraźniej pokazuje intencję zapytania.

USING skraca zapis w PostgreSQL, a SQL Server wymaga ON

Jeśli obie tabele łączy kolumna o tej samej nazwie, PostgreSQL pozwala zapisać warunek przez USING:

SELECT c.CustomerId, c.Name, o.OrderId
FROM Customers AS c
LEFT JOIN Orders AS o USING (CustomerId);

W wyniku kolumna łączenia pojawi się raz. Przy ON wybierasz kolumny z obu tabel osobno, co bywa wygodniejsze, gdy mają podobne nazwy albo potrzebujesz obu wartości. Dokumentacja PostgreSQL opisuje tę różnicę w wyniku, a składnia Transact-SQL dla SQL Server pokazuje warunek łączenia zapisany przez ON.

Jeśli kod ma działać na różnych silnikach, wybieraj ON i jawne kolumny w SELECT. Zapis jest dłuższy, ale łatwiej zauważyć, skąd pochodzi każde pole i które tabele łączysz.

Przed wdrożeniem sprawdź brak, jedno i wiele dopasowań

Najlepsza kontrola zapytania obejmuje trzy przypadki: rekord lewej tabeli bez odpowiednika, rekord z jednym dopasowaniem oraz rekord z kilkoma dopasowaniami. Taki zestaw od razu pokaże, czy zachowanie NULL i liczba wierszy odpowiadają zamierzonemu raportowi.

Potem sprawdź, czy agregujesz właściwą kolumnę i czy filtry po prawej stronie są w odpowiednim miejscu. Dzięki temu wynik pozostanie czytelny zarówno w raporcie, jak i po przekazaniu danych dalej, na przykład do aplikacji .NET.

FAQ - Najczęstsze pytania

Każde pasujące zamówienie tworzy osobny wiersz dla klienta. Jeśli jeden klient ma trzy zamówienia, pojawią się trzy wiersze z jego danymi. Klient bez zamówień nadal zajmie jeden wiersz, a kolumny zamówienia będą miały wartość NULL.

Dodaj warunek statusu, na przykład o.Status = 'Paid', w klauzuli ON. Ograniczy to pasujące zamówienia, ale zachowa klientów bez opłaconego zamówienia. Umieszczenie tego warunku w WHERE odfiltruje wiersze, w których status ma wartość NULL.

Użyj LEFT JOIN, a następnie w WHERE sprawdź, czy klucz zamówienia, taki jak o.OrderId, ma wartość IS NULL. Wybierz klucz główny lub inną kolumnę NOT NULL, aby odróżnić brak dopasowanego rekordu od pustej wartości w istniejącym zamówieniu. Alternatywnie użyj NOT EXISTS.

Grupuj wynik po identyfikatorze i nazwie klienta, a zamówienia licz przez COUNT(o.OrderId), zakładając, że OrderId nie dopuszcza NULL. COUNT(*) policzyłoby zachowany wiersz klienta bez zamówień jako 1.

Oceń artykuł

Ocena: 4.00 Liczba głosów: 1

Tagi

left join
agregacja
null
not exists
Autor Radosław Krajewski
Radosław Krajewski
Nazywam się Radosław Krajewski i od 6 lat zgłębiam tajniki programowania .NET, chmury Azure oraz sztucznej inteligencji. Moja przygoda z tymi technologiami zaczęła się od fascynacji tym, jak złożone problemy można rozwiązywać za pomocą kodu i innowacyjnych narzędzi. Staram się przekazywać tę wiedzę w sposób zrozumiały, dzieląc się swoimi doświadczeniami i spostrzeżeniami na kursdotnet.pl. W moich artykułach skupiam się na praktycznych aspektach, porównuję różne rozwiązania i analizuję najnowsze trendy, aby dostarczyć Wam rzetelne i aktualne informacje, które pomogą Wam rozwijać się w tej dynamicznie zmieniającej się dziedzinie.

Udostępnij artykuł

Napisz komentarz