Dataczwartek, 13 sierpnia 2026 Czas17:36:27
← Bazy danych i SQL

Lekcja 8. Podzapytania (SUBQUERIES) w SQL

Trudny / egzaminacyjny

Po co się tego uczymy?

Niektórych pytań nie da się zadać bazie danych jednym prostym SELECT-em — np. "pokaż produkty droższe niż średnia" wymaga najpierw policzenia tej średniej. Podzapytania pozwalają połączyć kilka kroków rozumowania w jedno zapytanie.

Teoria

Wyobraź sobie pytanie: "Pokaż wszystkich klientów, którzy wydali więcej niż WYNOSI ŚREDNIA wartość zamówienia w całym sklepie." Żeby to policzyć, potrzebujesz dwóch kroków: najpierw policzyć średnią, a potem porównać do niej każdego klienta. Właśnie to robi podzapytanie (ang. subquery) — zapytanie SQL wstawione w innym zapytaniu.

Podzapytanie jest wykonywane NAJPIERW, a jego wynik trafia do zapytania nadrzędnego. Najczęściej pojawia się w klauzuli WHERE:

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

Operator w takim porównaniu może być:
= lub >/< — gdy podzapytanie zwraca dokładnie jedną wartość,
IN — gdy podzapytanie zwraca listę wartości,
EXISTS — gdy interesuje Cię tylko, czy podzapytanie w ogóle coś zwróciło.

Przykłady:

-- produkty droższe niż średnia cena wszystkich produktów
SELECT nazwa, cena FROM Produkty
WHERE cena > (SELECT AVG(cena) FROM Produkty);

-- klienci, którzy CHOĆ RAZ coś zamówili
SELECT imie, nazwisko FROM Klienci
WHERE id_klienta IN (SELECT id_klienta FROM Zamowienia);

Przykład z życia

Panel "bestsellery" w sklepie internetowym często opiera się na podzapytaniu: najpierw baza liczy średnią sprzedaż wszystkich produktów, a potem wybiera te, które sprzedają się LEPIEJ niż ta średnia.

Kod (SQL)

-- klienci, którzy wydali więcej niż średnia wartość zamówienia w sklepie

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.id_klienta
HAVING SUM(z.ilosc * p.cena) > (
    SELECT AVG(ilosc * cena)
    FROM Zamowienia
    JOIN Produkty ON Zamowienia.id_produktu = Produkty.id_produktu
);

-- produkty, których NIGDY nikt nie zamówił

SELECT nazwa FROM Produkty
WHERE id_produktu NOT IN (SELECT id_produktu FROM Zamowienia);

Komentarz i wyjaśnienie kodu

W pierwszym zapytaniu podzapytanie w HAVING liczy średnią wartość WSZYSTKICH pozycji zamówień w całym sklepie — to jedna liczba. Zapytanie nadrzędne grupuje zamówienia po kliencie i porównuje sumę każdego klienta do tej jednej, wspólnej średniej. W drugim zapytaniu używamy NOT IN z podzapytaniem, które zwraca LISTĘ identyfikatorów produktów — wybieramy te produkty, których NIE MA na tej liście, czyli takie, które nigdy się nie pojawiły w żadnym zamówieniu.

Ćwiczenie samodzielne

Napisz podzapytanie znajdujące ucznia (uczniów) z najwyższą średnią ocen w tabeli Uczniowie (podpowiedź: WHERE srednia_ocen = (SELECT MAX(srednia_ocen) FROM Uczniowie)).

Zadania do pracy własnej

  1. Zadanie 1. Podzapytania w sklepie muzycznym

    Uwaga przed importem: plik z bazą jest spakowany jako .gz. phpMyAdmin w XAMPP przyjmuje takie pliki bez rozpakowywania. Jeżeli po pobraniu nazwa wygląda np. sklep_muzyczny.sql_.gz i import nie przejdzie, zmień nazwę pliku na sklep_muzyczny.sql.gz, a potem wybierz go w zakładce Importuj.

    Baza do pracy: Sklep muzyczny (.sql.gz). Zaimportuj spakowany plik SQL w XAMPP/phpMyAdmin, a następnie wykonaj zapytania.

    1. Wyświetl produkty droższe niż średnia cena produktu.
    2. Wyświetl klientów, którzy złożyli co najmniej jedno zamówienie.
    3. Wyświetl klientów, którzy nie złożyli żadnego zamówienia.
    4. Wyświetl produkt lub produkty o najwyższej cenie.
    5. Wyświetl zamówienia o wartości większej niż średnia wartość zamówienia.
    6. Wyświetl kategorie, w których istnieje produkt droższy niż 1000 zł.
    7. Wyświetl klientów z miasta, z którego pochodzi najwięcej klientów.
    8. Wyświetl produkty, których cena jest większa niż średnia cena w ich kategorii.
    9. Wyświetl klientów, którzy zamówili produkt z kategorii Instrumenty.
    10. Wyświetl produkty, których nie ma w żadnym zamówieniu.
  2. Zadanie 2. Podzapytania w aptece

    Uwaga przed importem: plik z bazą jest spakowany jako .gz. phpMyAdmin w XAMPP przyjmuje takie pliki bez rozpakowywania. Jeżeli po pobraniu nazwa wygląda np. sklep_muzyczny.sql_.gz i import nie przejdzie, zmień nazwę pliku na sklep_muzyczny.sql.gz, a potem wybierz go w zakładce Importuj.

    Baza do pracy: Apteka Zdrowie (.sql.gz). Zaimportuj spakowany plik SQL w XAMPP/phpMyAdmin, a następnie wykonaj zapytania.

    1. Wyświetl wszystkie leki wraz z kategorią i producentem.
    2. Wyświetl leki, których stan magazynowy jest mniejszy niż 50.
    3. Policz liczbę leków w każdej kategorii.
    4. Oblicz średnią cenę leków według kategorii.
    5. Oblicz wartość magazynu każdego leku: cena * ilość_magazyn.
    6. Pokaż trzy najdroższe leki.
    7. Wyświetl sprzedaż: nazwa leku, ilość, data, wartość sprzedaży.
    8. Oblicz sumę sprzedanych sztuk dla każdego leku.
    9. Pokaż producentów, których leki były sprzedawane.
    10. Pokaż kategorie, w których średnia cena przekracza 20 zł.
  3. Zadanie 3. Podzapytania w siłowni

    Uwaga przed importem: plik z bazą jest spakowany jako .gz. phpMyAdmin w XAMPP przyjmuje takie pliki bez rozpakowywania. Jeżeli po pobraniu nazwa wygląda np. sklep_muzyczny.sql_.gz i import nie przejdzie, zmień nazwę pliku na sklep_muzyczny.sql.gz, a potem wybierz go w zakładce Importuj.

    Baza do pracy: Siłownia FitZone (.sql.gz). Zaimportuj spakowany plik SQL w XAMPP/phpMyAdmin, a następnie wykonaj zapytania.

    1. Wyświetl zajęcia wraz z imieniem i nazwiskiem trenera.
    2. Wyświetl klientów z karnetem Premium.
    3. Policz liczbę zajęć prowadzonych przez każdego trenera.
    4. Policz zapisy na każde zajęcia.
    5. Wyświetl klientów zapisanych na Trening siłowy.
    6. Pokaż zajęcia rozpoczynające się po godzinie 17:00.
    7. Pokaż klientów, którzy zapisali się na więcej niż jedne zajęcia.
    8. Wyświetl raport: klient, typ karnetu, zajęcia, trener, data zapisu.
    9. Policz zapisy według dnia.
    10. Pokaż trenerów, którzy prowadzą zajęcia z co najmniej dwoma zapisami.

Typowe błędy

Częsty błąd to użycie operatora = z podzapytaniem, które może zwrócić WIĘCEJ niż jedną wartość — SQL zgłosi wtedy błąd; w takiej sytuacji trzeba użyć IN zamiast =. Drugi błąd to pomylenie IN z NOT IN, co odwraca sens całego zapytania. Trzeci: pisanie bardzo zagnieżdżonych podzapytań (podzapytanie w podzapytaniu w podzapytaniu), które stają się nieczytelne — czasem lepiej rozbić problem na dwa osobne zapytania i połączyć wyniki w aplikacji.

Nawiązanie do egzaminu zawodowego

Podzapytania to zaawansowany, ale często sprawdzany element efektu "wyszukuje informacje w bazie danych przy użyciu języka SQL" (INF.03.4) — pojawiają się w trudniejszych zadaniach praktycznych, gdzie trzeba połączyć warunek z wynikiem innego zapytania.