Podzapytania (SUBQUERIES) w SQL

Wyobraź sobie, że chcesz zapytać:
„Pokaż wszystkich klientów, którzy wydali więcej niż średnia wartość zamówienia w sklepie.”

Żeby to zrobić, potrzebujesz dwóch zapytań:

  1. jedno, które obliczy średnią wartość zamówienia,
  2. drugie, które wybierze klientów — i porówna ich wyniki do tej średniej.

To właśnie przykład podzapytania – czyli zapytania w zapytaniu.


1. Co to jest podzapytanie?

Podzapytanie (ang. subquery) to zapytanie SQL wstawione w innym zapytaniu.
Działa jak „mini-zapytanie”, które dostarcza dane do zapytania głównego.

Najczęściej pojawia się w klauzulach:

  • WHERE,
  • FROM,
  • SELECT.

Podzapytanie jest wykonywane najpierw, a jego wynik przekazywany jest do zapytania nadrzędnego.


2. Podstawowa składnia

SELECT kolumny
FROM tabela
WHERE kolumna OPERATOR (SELECT kolumny FROM inna_tabela WHERE warunek);

Operator może być np.:

  • = – dokładne dopasowanie,
  • > lub < – porównanie,
  • IN – sprawdzenie, czy wartość występuje w zestawie,
  • ANY, ALL, EXISTS – zaawansowane warunki logiczne.

3. Przykłady na bazie sklep_muzyczny

a) Klienci, którzy wydali więcej niż średnia wszystkich zakupów

SELECT k.imie, k.nazwisko, SUM(z.ilosc * p.cena) AS wartosc
FROM Klienci k
JOIN Zamowienia z ON k.id_klienta = z.id_klienta
JOIN Produkty p ON z.id_produktu = p.id_produktu
GROUP BY k.imie, k.nazwisko
HAVING wartosc > (
    SELECT AVG(z2.ilosc * p2.cena)
    FROM Zamowienia z2
    JOIN Produkty p2 ON z2.id_produktu = p2.id_produktu
);

Wewnątrz HAVING znajduje się podzapytanie, które oblicza średnią wartość wszystkich zakupów.
Zapytanie główne wybiera tylko tych klientów, którzy wydali więcej niż ta średnia.


b) Produkty droższe niż średnia cena wszystkich produktów

SELECT nazwa, cena
FROM Produkty
WHERE cena > (SELECT AVG(cena) FROM Produkty);

Podzapytanie (SELECT AVG(cena) FROM Produkty) zwraca jedną wartość – średnią cenę.
Główne zapytanie porównuje do niej każdą cenę produktu.


c) Produkty kupione przez klienta o nazwisku „Kowalski”

SELECT nazwa
FROM Produkty
WHERE id_produktu IN (
    SELECT id_produktu
    FROM Zamowienia z
    JOIN Klienci k ON z.id_klienta = k.id_klienta
    WHERE k.nazwisko = 'Kowalski'
);

Podzapytanie znajduje id_produktu z zamówień Kowalskiego,
a główne zapytanie wypisuje ich nazwy.


d) Klienci, którzy nie złożyli żadnego zamówienia

SELECT imie, nazwisko
FROM Klienci
WHERE id_klienta NOT IN (
    SELECT id_klienta FROM Zamowienia
);

Podzapytanie tworzy listę wszystkich klientów, którzy coś zamówili.
Zapytanie główne wybiera tych, których nie ma na tej liście.


e) Produkt z najwyższą ceną (bez użycia MAX)

SELECT nazwa, cena
FROM Produkty
WHERE cena = (
    SELECT cena FROM Produkty ORDER BY cena DESC LIMIT 1
);

Podzapytanie znajduje największą cenę,
a główne zapytanie pokazuje produkt, który ją ma.


4. Podzapytania w klauzuli FROM

Podzapytanie może działać jak tymczasowa tabela.

Przykład:

Oblicz średnią wartość zamówienia dla każdego klienta i pokaż tylko tych, których wynik przekracza 500 zł.

SELECT imie, nazwisko, wartosc
FROM (
    SELECT k.imie, k.nazwisko, SUM(z.ilosc * p.cena) AS wartosc
    FROM Klienci k
    JOIN Zamowienia z ON k.id_klienta = z.id_klienta
    JOIN Produkty p ON z.id_produktu = p.id_produktu
    GROUP BY k.imie, k.nazwisko
) AS podsumowanie
WHERE wartosc > 500;

Wewnętrzne zapytanie tworzy tabelę podsumowanie, a zewnętrzne wybiera z niej dane według warunku.


5. Podzapytania w klauzuli SELECT

Czasem chcemy dodać do wyników dodatkową informację obliczoną dynamicznie.

Przykład:

Dla każdego produktu pokaż średnią cenę wszystkich produktów (dla porównania):

SELECT nazwa, cena,
       (SELECT AVG(cena) FROM Produkty) AS srednia_cena
FROM Produkty;

Każdy wiersz będzie zawierał swoją cenę i średnią cenę wszystkich produktów.


6. Rodzaje podzapytań

RodzajWynikUżycie
Jednowartościowe (scalar)Zwraca jedną wartośćWHERE cena > (SELECT AVG(cena) FROM Produkty)
Wielowartościowe (IN)Zwraca wiele wartościWHERE id IN (SELECT id_klienta FROM Zamowienia)
ZagnieżdżoneWewnątrz innych podzapytańSELECT ... WHERE x IN (SELECT ... WHERE y IN (...))
W klauzuli FROMDziała jak tymczasowa tabelaFROM (SELECT ...) AS tymczasowa

7. Zadania praktyczne

Zadanie 1 – ceny powyżej średniej

Wyświetl wszystkie produkty, których cena jest większa niż średnia cena wszystkich produktów.


Zadanie 2 – klienci aktywni

Wyświetl imiona i nazwiska klientów, którzy złożyli zamówienia (czyli ich id_klienta znajduje się w tabeli Zamowienia).


Zadanie 3 – klienci bez zamówień

Wyświetl imiona i nazwiska klientów, którzy nie złożyli żadnego zamówienia (użyj NOT IN i podzapytania).


Zadanie 4 – najlepsi klienci

Wyświetl klientów, których suma zakupów przekracza średnią wszystkich sum zakupów.


Zadanie 5 – porównanie produktów

Dla każdego produktu pokaż jego cenę oraz średnią cenę wszystkich produktów (jako nową kolumnę srednia_cena).


Zadanie 6 – dla ambitnych

Pokaż nazwę najdroższego produktu w każdej kategorii (użyj podzapytania w klauzuli WHERE).


8. Typowe błędy uczniów

❌ Zapomniany nawias w podzapytaniu
❌ Użycie = zamiast IN przy wielu wynikach
❌ Brak aliasu przy podzapytaniu w FROM
❌ Mylenie kolejności wykonania (najpierw wykonuje się podzapytanie!)


9. Podsumowanie

KlauzulaZastosowanie podzapytaniaPrzykład
WHEREfiltrowanie danychWHERE cena > (SELECT AVG(cena) FROM Produkty)
IN / NOT INsprawdzanie listy wartościWHERE id IN (SELECT id_klienta FROM Zamowienia)
FROMtymczasowa tabelaFROM (SELECT ...) AS T
SELECTdodatkowe kolumny z obliczeniamiSELECT cena, (SELECT AVG(cena) FROM Produkty)

Podzapytania to potężne narzędzie, które pozwala łączyć analizę danych z logiką programowania.
Dzięki nim możesz pisać jedno zapytanie, które robi kilka rzeczy naraz — bez konieczności tworzenia nowych tabel czy procedur.
To także ważny element egzaminu INF.03, gdzie często trzeba stworzyć raport oparty o dane z kilku tabel z warunkami i obliczeniami.