Lekcja 8. Podzapytania (SUBQUERIES) w SQL
Trudny / egzaminacyjnyPo 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
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_.gzi import nie przejdzie, zmień nazwę pliku nasklep_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.
- Wyświetl produkty droższe niż średnia cena produktu.
- Wyświetl klientów, którzy złożyli co najmniej jedno zamówienie.
- Wyświetl klientów, którzy nie złożyli żadnego zamówienia.
- Wyświetl produkt lub produkty o najwyższej cenie.
- Wyświetl zamówienia o wartości większej niż średnia wartość zamówienia.
- Wyświetl kategorie, w których istnieje produkt droższy niż 1000 zł.
- Wyświetl klientów z miasta, z którego pochodzi najwięcej klientów.
- Wyświetl produkty, których cena jest większa niż średnia cena w ich kategorii.
- Wyświetl klientów, którzy zamówili produkt z kategorii Instrumenty.
- Wyświetl produkty, których nie ma w żadnym zamówieniu.
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_.gzi import nie przejdzie, zmień nazwę pliku nasklep_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.
- Wyświetl wszystkie leki wraz z kategorią i producentem.
- Wyświetl leki, których stan magazynowy jest mniejszy niż 50.
- Policz liczbę leków w każdej kategorii.
- Oblicz średnią cenę leków według kategorii.
- Oblicz wartość magazynu każdego leku: cena * ilość_magazyn.
- Pokaż trzy najdroższe leki.
- Wyświetl sprzedaż: nazwa leku, ilość, data, wartość sprzedaży.
- Oblicz sumę sprzedanych sztuk dla każdego leku.
- Pokaż producentów, których leki były sprzedawane.
- Pokaż kategorie, w których średnia cena przekracza 20 zł.
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_.gzi import nie przejdzie, zmień nazwę pliku nasklep_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.
- Wyświetl zajęcia wraz z imieniem i nazwiskiem trenera.
- Wyświetl klientów z karnetem Premium.
- Policz liczbę zajęć prowadzonych przez każdego trenera.
- Policz zapisy na każde zajęcia.
- Wyświetl klientów zapisanych na Trening siłowy.
- Pokaż zajęcia rozpoczynające się po godzinie 17:00.
- Pokaż klientów, którzy zapisali się na więcej niż jedne zajęcia.
- Wyświetl raport: klient, typ karnetu, zajęcia, trener, data zapisu.
- Policz zapisy według dnia.
- 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.