Zapytanie w zapytaniu i pułapka NOT IN z NULL, widok jako zapisane zapytanie, indeks przyspieszający wyszukiwanie kosztem zapisu oraz transakcja z COMMIT i ROLLBACK. Lekcja 8.7 kursu INF.03.
Cztery narzędzia, które odróżniają zapytanie napisane „na piechotę" od zapytania napisanego świadomie. Podzapytanie wstawia wynik jednego zapytania do drugiego, widok zapamiętuje zapytanie pod nazwą, indeks przyspiesza wyszukiwanie, a transakcja pilnuje, żeby kilka zmian albo weszło w całości, albo wcale. Na egzaminie pisemnym każde z nich ma swój typ pytania, a najczęstsze dotyczy indeksu: co przyspiesza, a co spowalnia.
Tabele do przykładów
Tabela klient:
Tabela zamowienie (bez kolumn produkt_id i ilosc, których ta lekcja nie używa):
Podzapytanie: zapytanie w zapytaniu
Podzapytanie to SELECT zamknięty w nawiasie, którego wynik zostaje użyty przez zapytanie zewnętrzne. Wykonuje się jako pierwsze.
Jedna wartość. Podzapytanie zwraca jedną liczbę, więc można ją porównać przez = albo >:
SELECT nazwa, cena FROM produkt
WHERE cena > (SELECT AVG(cena) FROM produkt);
Baza liczy najpierw średnią (366), potem wybiera produkty droższe. Tak zapisuje się pytanie „droższe niż średnia", którego nie da się rozwiązać jednym WHERE, bo WHERE cena > AVG(cena) jest niedozwolone.
Lista wartości. Podzapytanie zwraca kolumnę, więc porównuje się przez IN:
SELECT imie FROM klient
WHERE id IN (SELECT klient_id FROM zamowienie);
Wynik: Anna, Piotr. To samo pytanie można zadać złączeniem, a IN bywa czytelniejsze, gdy z drugiej tabeli nie potrzebujesz żadnej kolumny.
Pułapka: NOT IN przestaje działać, gdy w podzapytaniu pojawi się NULL. WHERE id NOT IN (SELECT klient_id FROM zamowienie) zwraca PUSTY wynik, bo w kolumnie klient_id jest NULL, a porównanie z nieznaną wartością nigdy nie jest prawdziwe. Ratunek: WHERE klient_id IS NOT NULL w podzapytaniu albo LEFT JOIN ... WHERE ... IS NULL.
Podzapytanie w FROM. Wynik podzapytania działa jak tymczasowa tabela i musi dostać alias:
SELECT AVG(suma) AS srednie_zamowienie
FROM (SELECT klient_id, SUM(kwota) AS suma
FROM zamowienie GROUP BY klient_id) AS podsumowanie;
Podzapytanie może też stać w SELECT, jako dodatkowa kolumna wyliczana dla każdego wiersza. Taki zapis nazywa się podzapytaniem skorelowanym, bo odwołuje się do wiersza z zapytania zewnętrznego, i jest wolny przy dużych tabelach.
W przeglądarce usuń z podzapytania warunek IS NOT NULL i wykonaj zapytanie ponownie: wynik zrobi się pusty.
Widok: zapisane zapytanie
Widok (VIEW) to zapytanie zapamiętane pod nazwą. Zachowuje się jak tabela, ale nie przechowuje danych: przy każdym użyciu wykonuje się zapytanie, z którego powstał.
CREATE VIEW zamowienia_klientow AS
SELECT k.imie, k.miasto, z.kwota
FROM klient k
INNER JOIN zamowienie z ON z.klient_id = k.id;
SELECT * FROM zamowienia_klientow WHERE miasto = 'Kraków';
DROP VIEW zamowienia_klientow;
Po co widok:
›
Skraca powtarzane zapytanie. Zamiast pisać złączenie trzech tabel w dziesięciu miejscach, piszesz je raz.
›
Ogranicza dostęp. Użytkownikowi można nadać prawo do widoku, a nie do tabeli, i wtedy zobaczy tylko wybrane kolumny. Na przykład listę pracowników bez kolumny z pensją.
›
Ukrywa złożoność. Kto korzysta z widoku, nie musi wiedzieć, z ilu tabel powstał.
Widok jest zawsze aktualny, bo dane pobiera z tabel w momencie użycia. Zmiana danych przez widok jest możliwa tylko w prostych przypadkach (jedna tabela, bez grupowania).
Indeks: szybciej czytać, wolniej pisać
Indeks to dodatkowa struktura, w której baza trzyma posortowane wartości kolumny razem ze wskazaniem na wiersz. Działa jak skorowidz w książce: zamiast czytać wszystkie strony, zaglądasz do spisu i idziesz na właściwą.
CREATE INDEX idx_nazwa ON produkt (nazwa);
DROP INDEX idx_nazwa ON produkt;
przyspiesza WHERE, ORDER BY i złączenia na indeksowanej kolumnie
krótszy czas wyszukiwania
spowalnia INSERT, UPDATE i DELETE
przy każdym zapisie trzeba zaktualizować także indeks
zajmuje miejsce na dysku
im więcej indeksów, tym większa baza
Stąd zasada: indeksuje się kolumny, po których często SZUKASZ, a nie wszystkie. Klucz główny i kolumna UNIQUE dostają indeks automatycznie, więc nie trzeba go tworzyć ręcznie. Klucz obcy w InnoDB także.
Pułapka: pytanie brzmi zwykle „co przyspiesza wyszukiwanie danych, ale może spowolnić operacje zapisu". Odpowiedź to indeks, nie widok. Widok niczego nie przyspiesza, bo to tylko zapisane zapytanie.
Transakcja: wszystko albo nic
Transakcja łączy kilka poleceń w jedną całość. Albo wykonają się wszystkie, albo żadne. Klasyczny przykład to przelew: jeżeli pieniądze zniknęły z jednego konta, muszą pojawić się na drugim.
START TRANSACTION;
UPDATE konto SET saldo = saldo - 100 WHERE id = 1;
UPDATE konto SET saldo = saldo + 100 WHERE id = 2;
COMMIT; -- zatwierdza obie zmiany naraz
COMMIT (ZATWIERDŹ) zapisuje zmiany na stałe. ROLLBACK (WYCOFAJ) cofa wszystko, co zaszło od START TRANSACTION:
START TRANSACTION;
DELETE FROM produkt WHERE kategoria = 'monitory';
ROLLBACK; -- wiersze wracają, jakby nic się nie stało
Cztery cechy transakcji określa skrót ACID: Atomicity (niepodzielność: wszystko albo nic), Consistency (spójność: baza przed i po jest poprawna), Isolation (izolacja: równoległe transakcje sobie nie przeszkadzają), Durability (trwałość: po COMMIT zmiana przetrwa awarię).
W MySQL transakcje obsługuje silnik InnoDB, domyślny od wersji 5.5. Starszy MyISAM ich nie obsługuje i po prostu wykonuje każde polecenie od razu; to samo dotyczy kluczy obcych. Stare pytania z banku pytają o MyISAM jako silnik domyślny, co dziś nie jest już prawdą.
Na egzaminie
Pytania pisemne: „Co przyspiesza wyszukiwanie, a spowalnia zapis" (indeks), „Które polecenie cofa zmiany w transakcji" (ROLLBACK), „Czym jest widok" (zapisane zapytanie, nie kopia danych), rachunek z podzapytaniem, gdzie trzeba najpierw policzyć wynik wewnętrznego SELECT.
Informator CKE wymienia podzapytania wprost przy zadaniu praktycznym, ale w typowym arkuszu cztery kwerendy da się napisać bez nich. Podzapytanie przydaje się, gdy w opisie pojawia się słowo „najdroższy", „powyżej średniej" albo „którzy nie mają żadnego".
Ściąga
podzapytanie z jedną wartością
WHERE cena > (SELECT AVG(cena) FROM produkt)
wykonuje się pierwsze
podzapytanie z listą
WHERE id IN (SELECT klient_id FROM zamowienie)
NOT IN psuje NULL
podzapytanie jako tabela
FROM (SELECT ...) AS alias
alias obowiązkowy
widok
CREATE VIEW nazwa AS SELECT ...
zapisane zapytanie, zero danych
indeks
CREATE INDEX idx ON tabela (kolumna)
szybszy odczyt, wolniejszy zapis
transakcja
START TRANSACTION; ... COMMIT;
ROLLBACK cofa całość
ACID
niepodzielność, spójność, izolacja, trwałość
InnoDB tak, MyISAM nie
Pobierz ściągę PDF