Język SQL: Podzapytania

article-thumbnail

Umiemy już stworzyć proste zapytanie w języku SQL, jednak w niektórych typach zadań jest to wiedza niewystarczająca. Rozwiązaniem niektórych problemów, może być wykorzystanie podzapytania. Na początek zdefiniujmy czym ów podzapytanie w języku SQL jest. Otóż jest to zapytanie, na podstawie którego inne zapytania zwracają poprawny wynik. Można też zdefiniować je jako zapytanie zagnieżdżone w innym.

Sugeruję stosować podzapytania w trzech przypadkach. Po pierwsze, aby uniknąć łączenia tabel za pomocą INNER JOIN, ma to na celu oszczędzenie zasobów i odciążenie bazy danych. Wykonanie kwerendy zagnieżdżonej kosztuje znacznie mniej zasobów niż łączenie tabel (zwłaszcza większych). Po drugie Możemy łatwo ograniczać ilość danych która dociera do zapytania głównego. Po trzecie natomiast, możemy zastosować podzapytanie razem z poleceniami modyfikującymi zawartość bazy danych.

Baza danych i tabele.

Na potrzeby prezentacji działania i składni podzapytań, będę używał tabel z bazy danych z arkusza maturalnego z Maja 2021 r. Składa się ona z trzech tabel:

gracze_txt:

Struktura tabeli gracze_txt, używana do podzapytań SQL

jednostki_txt

Struktura tabeli jednostki_txt używana do podzapytań SQL

klasy_txt

Struktura tabeli klasy_txt używana do podzapytań SQL

Zastosowanie 1 – alternatywa dla INNER JOIN

Załóżmy przykładowe polecenie: Znajdź wszystkie jednostki których atrybut magia jest równy 0. Podaj identyfikatory jednostki i gracza, oraz nazwę.

Możemy to rozwiązać na dwa sposoby, Pierwszym z nich jest łączenie tabel poprzez INNER JOIN:

SELECT `id_jednostki`,`id_gracza`,`jednostki_txt`.`nazwa` FROM `jednostki_txt` 
INNER JOIN `klasy_txt` ON `klasy_txt`.`nazwa` = `jednostki_txt`.`nazwa` 
WHERE `klasy_txt`.`magia` = 0;

Dla tej metody czas wykonania zapytania wyniósł u mnie ok 0,0096s. Niby nie dużo. Zobaczmy jednak jak będzie wyglądało to przy zastosowaniu podzapytania:

SELECT `id_jednostki`,`id_gracza`,`nazwa` FROM `jednostki_txt` 
WHERE `nazwa` IN (SELECT `nazwa` FROM `klasy_txt` WHERE `magia`=0);

Czas wykonania to zaledwie 0,0006s, oznacza to podzapytanie jest ok 16 razy szybsze (w tym wypadku). Oczywiście przy tak małej tabeli różnica nie powala, ale wraz z rozrostem baz danych będzie coraz istotniejsza.

Zastosowanie 2 – Zastępowanie tabel

Przykładowe zadanie: Posortuj jednostki według odległości od punku x=100 y=100. Do zrealizowania tego zadania potrzebujemy 2 zbiory danych. Pierwszy z nich to informacja o odległości danej jednostki (dane które wyświetlimy), a drugi to dane na ich pozycji (służące do realizacji logiki zadania). W tej sytuacji mogę oddzielić logikę zapytania od wyświetlenia. Obliczeniami zajmie się podzapytanie, podczas gdy wyświetleniem zapytanie główne. Już patrząc na zapis widzimy, że wynik podzapytania zastępuje tu normalną tabelę.

SELECT `id_jednostki`, `odleglosc` FROM 
(SELECT `id_jednostki`, ABS(100-`lok_x`)+ABS(100-`lok_y`) as `odleglosc` FROM `jednostki_txt`) 
as `odleglosci` 
ORDER By `odleglosci`.`odleglosc` ASC;

Zastosowanie 3 – Współpraca z operacjami modyfikującymi bazy danych

Trzecim zastosowaniem podzapytania jest użycie go wraz z poleceniem edytującym tabelę. Przykładowe polecenie może wyglądać tak: Napisz kwerendę, która spowoduje zwiększenie parametru szybkość o 2 wszystkim klasom elfów, których ilość jednostek nie przekracza 100 .

UPDATE `klasy_txt` SET `szybkosc`=`szybkosc`+2 WHERE `nazwa` IN 
(SELECT `nazwa` as `ilosc` FROM `jednostki_txt` GROUP BY `jednostki_txt`.`nazwa` HAVING COUNT(*)<=100); 

Oczywiście w taki samo sposób możemy wykonywać polecenia DELETE.