Teoria — kluczowe polecenia
SELECT ... FROM ... WHERE
Podstawowe zapytanie pobierające dane. SELECT wskazuje kolumny, FROM — tabelę, a WHERE — warunek filtrowania rekordów.
INNER JOIN
Zwraca tylko te rekordy, które mają dopasowanie w obu tabelach. Jeśli rekord w jednej tabeli nie ma pary — jest pomijany.
LEFT JOIN
Zwraca wszystkie rekordy z tabeli lewej, nawet jeśli nie mają dopasowania w tabeli prawej. W takich przypadkach kolumny z prawej tabeli zawierają NULL.
GROUP BY + HAVING
GROUP BY grupuje wiersze o tych samych wartościach. HAVING filtruje grupy (jak WHERE, ale po agregacji).
Podzapytania (subquery)
Zapytanie zagnieżdżone wewnątrz innego — najczęściej w klauzuli WHERE ... IN (...). Wynik podzapytania jest używany przez zapytanie zewnętrzne.
Tabela porównania JOIN
| Typ JOIN | Zwraca | Rekordy bez pary | Kiedy używać? |
|---|---|---|---|
| INNER JOIN | Tylko rekordy powiązane w obu tabelach | ✗ pominięte | Gdy potrzebujesz tylko kompletnych danych |
| LEFT JOIN | Wszystkie z tabeli lewej + dopasowane z prawej | ✓ NULL w prawej | Gdy chcesz znaleźć "sieroty" lub zachować wszystkich |
| RIGHT JOIN | Wszystkie z tabeli prawej + dopasowane z lewej | ✓ NULL w lewej | Rzadko używany — zazwyczaj zastępuje LEFT JOIN |
Baza danych: GameVault
Wszystkie ćwiczenia korzystają z bazy GameVault — sklepu z grami cyfrowymi. Mamy cztery powiązane tabele:
-- Stwórz bazę i wybierz ją
CREATE DATABASE gamevault;
USE gamevault;
-- Tabela wydawców
CREATE TABLE wydawcy (
wydawca_id INT PRIMARY KEY AUTO_INCREMENT,
nazwa VARCHAR(100) NOT NULL,
kraj VARCHAR(50)
);
-- Tabela gier
CREATE TABLE gry (
gra_id INT PRIMARY KEY AUTO_INCREMENT,
tytul VARCHAR(100) NOT NULL,
gatunek VARCHAR(50),
cena DECIMAL(7,2) NOT NULL,
wydawca_id INT,
FOREIGN KEY (wydawca_id) REFERENCES wydawcy(wydawca_id)
);
-- Tabela graczy
CREATE TABLE gracze (
gracz_id INT PRIMARY KEY AUTO_INCREMENT,
nick VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100),
kraj VARCHAR(50)
);
-- Tabela zakupów
CREATE TABLE zakupy (
zakup_id INT PRIMARY KEY AUTO_INCREMENT,
gracz_id INT,
gra_id INT,
data_zakupu DATE DEFAULT (CURRENT_DATE),
cena_zakupu DECIMAL(7,2),
FOREIGN KEY (gracz_id) REFERENCES gracze(gracz_id),
FOREIGN KEY (gra_id) REFERENCES gry(gra_id)
);
-- Dane testowe
INSERT INTO wydawcy (nazwa, kraj) VALUES
('CD Projekt', 'PL'), ('Valve', 'US'), ('IndieSoft', 'DE');
INSERT INTO gry (tytul, gatunek, cena, wydawca_id) VALUES
('Cyberpunk 2077', 'RPG', 199.99, 1),
('Half-Life: Alyx', 'FPS', 149.99, 2),
('Portal 2', 'Puzzle', 39.99, 2),
('PixelDungeon', 'RPG', 19.99, 3),
('SpeedRacer VR', 'Racing', 89.99, 3);
INSERT INTO gracze (nick, email, kraj) VALUES
('ShadowWolf', 'wolf@gm.pl', 'PL'),
('NeonByte', 'neon@gm.us', 'US'),
('PixelQueen', 'queen@gm.de', 'DE'),
('NoobMaster', 'noob@gm.pl', 'PL');
INSERT INTO zakupy (gracz_id, gra_id, data_zakupu, cena_zakupu) VALUES
(1, 1, '2024-11-01', 199.99),
(1, 3, '2024-11-15', 39.99),
(2, 2, '2024-12-01', 149.99),
(3, 4, '2025-01-10', 19.99),
(3, 1, '2025-01-20', 199.99);
-- NoobMaster (gracz_id=4) nie ma żadnych zakupów
Ćwiczenia krok po kroku
Wyświetl tytuł i cenę wszystkich gier z gatunku RPG, posortowanych od najtańszej do najdroższej. Pokaż tylko gry tańsze niż 150 zł.
SELECT tytul, cena
FROM gry
WHERE gatunek = 'RPG'
AND cena < 150
ORDER BY cena ASC;
| tytul | cena |
|---|---|
| PixelDungeon | 19.99 |
Wyświetl nick gracza i tytuł każdej kupionej przez niego gry. Posortuj według nicka.
SELECT g.nick,
gr.tytul
FROM gracze g
INNER JOIN zakupy z ON g.gracz_id = z.gracz_id
INNER JOIN gry gr ON z.gra_id = gr.gra_id
ORDER BY g.nick ASC;
| nick | tytul |
|---|---|
| NeonByte | Half-Life: Alyx |
| PixelQueen | PixelDungeon |
| PixelQueen | Cyberpunk 2077 |
| ShadowWolf | Cyberpunk 2077 |
| ShadowWolf | Portal 2 |
Wyświetl wszystkich graczy — nawet tych, którzy nic nie kupili. Znajdź "nieaktywnych" graczy (zakup_id = NULL).
-- Wszyscy gracze z ich zakupami (lub NULL)
SELECT g.nick, z.zakup_id, z.data_zakupu
FROM gracze g
LEFT JOIN zakupy z ON g.gracz_id = z.gracz_id
ORDER BY g.nick;
-- Tylko nieaktywni gracze
SELECT g.nick, g.kraj
FROM gracze g
LEFT JOIN zakupy z ON g.gracz_id = z.gracz_id
WHERE z.zakup_id IS NULL;
| nick | kraj |
|---|---|
| NoobMaster | PL |
Oblicz łączne wydatki i liczbę zakupów każdego gracza. Wyświetl tylko tych, którzy wydali ponad 100 zł. Posortuj od najwyższych wydatków.
SELECT g.nick,
COUNT(z.zakup_id) AS liczba_zakupow,
SUM(z.cena_zakupu) AS lacznie_wydane
FROM gracze g
JOIN zakupy z ON g.gracz_id = z.gracz_id
GROUP BY g.gracz_id, g.nick
HAVING SUM(z.cena_zakupu) > 100
ORDER BY lacznie_wydane DESC;
| nick | liczba_zakupow | lacznie_wydane |
|---|---|---|
| ShadowWolf | 2 | 239.98 |
| PixelQueen | 2 | 219.98 |
| NeonByte | 1 | 149.99 |
Wyświetl nick gracza, tytuł gry i nazwę wydawcy dla wszystkich zakupów. Posortuj po nazwie wydawcy.
SELECT g.nick AS gracz,
gr.tytul AS gra,
w.nazwa AS wydawca
FROM gracze g
JOIN zakupy z ON g.gracz_id = z.gracz_id
JOIN gry gr ON z.gra_id = gr.gra_id
JOIN wydawcy w ON gr.wydawca_id = w.wydawca_id
ORDER BY w.nazwa, g.nick;
| gracz | gra | wydawca |
|---|---|---|
| PixelQueen | Cyberpunk 2077 | CD Projekt |
| ShadowWolf | Cyberpunk 2077 | CD Projekt |
| PixelQueen | PixelDungeon | IndieSoft |
| NeonByte | Half-Life: Alyx | Valve |
| ShadowWolf | Portal 2 | Valve |
Używając podzapytania, wyświetl nick i kraj graczy, którzy kupili grę z gatunku RPG.
SELECT nick, kraj
FROM gracze
WHERE gracz_id IN (
-- wewnętrzne zapytanie zwraca listę gracz_id
SELECT z.gracz_id
FROM zakupy z
JOIN gry gr ON z.gra_id = gr.gra_id
WHERE gr.gatunek = 'RPG'
)
ORDER BY nick;
| nick | kraj |
|---|---|
| PixelQueen | DE |
| ShadowWolf | PL |
Zadanie projektowe
Analiza danych GameVault
Korzystając z bazy GameVault, napisz zapytania SQL realizujące poniższe wymagania. Każde zapytanie zapisz w phpMyAdmin i sprawdź wynik.
- 01 Wyświetl nick i łączne wydatki każdego gracza z Polski (kraj = 'PL'), posortowane malejąco po wydatkach. Użyj JOIN i GROUP BY.
- 02 Wyświetl nazwy gier, których nikt jeszcze nie kupił. Użyj LEFT JOIN i IS NULL.
- 03 Wyświetl wydawców i łączną liczbę sprzedanych przez nich gier (COUNT). Posortuj od największej liczby. Użyj JOIN + GROUP BY.
- 04 Wyświetl nick gracza, tytuł gry i datę zakupu dla zakupów z roku 2025. Użyj INNER JOIN i WHERE z YEAR().
- 05 Używając podzapytania, znajdź graczy, którzy kupili grę droższą niż średnia cena wszystkich gier.
Każde zapytanie powinno być opisane komentarzem SQL (--). Zapisz kod w pliku .sql.