CTE to nazwane podzapytanie
Common Table Expression (CTE) to podzapytanie wyciągnięte na zewnątrz i opatrzone nazwą. Zamiast zagnieżdżać SELECT wewnątrz innego SELECT, definiujesz je na początku przez WITH, nadajesz mu nazwę i używasz tej nazwy w głównym zapytaniu jak tabeli.
Kształt jest zawsze taki sam:
Czytaj od góry do dołu: najpierw zbuduj nazwany wynik customer_totals, potem odpytaj ten wynik. CTE zachowuje się jak tymczasowy widok, który istnieje tylko przez czas wykonania tej jednej instrukcji.
To samo zapytanie bez CTE
Oto ta sama logika zapisana jako podzapytanie, żeby było widać, co zastępuje CTE:
Ta sama odpowiedź. Ale zwróć uwagę na kolejność czytania: wzrok musi zanurkować w nawiasy, zrozumieć, co jest liczone, i wrócić na zewnątrz. Wersja z CTE czyta się w kolejności, w jakiej wykonuje się praca: zdefiniuj wynik pośredni, a potem go użyj. Przy małym zapytaniu to bez różnicy. Przy zapytaniu z trzema czy czterema krokami to różnica między kodem, który się przegląda, a kodem, który trzeba rozszyfrować.
Kilka CTE w jednym zapytaniu
Możesz połączyć kilka CTE, oddzielając je przecinkami. Każde może odwoływać się do zdefiniowanych przed nim, więc budujesz potok nazwanych kroków:
Jedno WITH, a potem definicje CTE oddzielone przecinkami. Drugie CTE (big_spenders) czyta z pierwszego (customer_totals) tak jak z tabeli. Główny SELECT następuje po definicji ostatniego CTE.
Częsty błąd: ponowne wpisanie WITH przed drugim CTE. Nie rób tego, to błąd składni. Jedno WITH obejmuje wszystkie.
Wielokrotne odwołania do CTE
Tu CTE naprawdę wyprzedzają podzapytania. Jeśli ten sam wynik pośredni jest potrzebny w dwóch miejscach, CTE pozwala obliczyć go raz i odwołać się do niego dwa razy:
Do CTE są dwa odwołania: raz, aby obliczyć średnią, i raz jako główne źródło. Bez CTE trzeba by powielić zapytanie z GROUP BY, a każdą zmianę wprowadzać w dwóch miejscach.
CTE z INSERT, UPDATE i DELETE
CTE nie służą tylko do SELECT. Klauzulę WITH możesz postawić przed INSERT, UPDATE lub DELETE, aby użyć nazwanego podzapytania przy zapisie:
CTE opisuje, które wiersze oznaczyć. INSERT ... SELECT używa go jako źródła. Ta sama sztuczka działa z DELETE FROM ... WHERE id IN (SELECT id FROM cte) przy etapowym usuwaniu, gdy logika wyboru wierszy jest zawiła.
Kiedy sięgać po CTE
Kilka praktycznych zasad:
- Zapytanie ma więcej niż jeden krok logiczny. Agregacja, potem filtrowanie po agregacie, potem złączenie wyniku: to potok, a jedno CTE na krok czyni go czytelnym.
- W przeciwnym razie powtarzasz to samo podzapytanie. Zdefiniuj je raz, odwołaj się dwa razy.
- Podzapytanie zasługuje na nazwę. Jeśli nad podzapytaniem przydałby się komentarz wyjaśniający, co przedstawia, nazwa CTE jest tym komentarzem, a do tego pilnuje jej składnia.
- Zaraz napiszesz zapytanie rekurencyjne. To możliwe tylko z
WITH RECURSIVE, omówionym na następnej stronie.
Kiedy nie warto się trudzić:
- Pojedyncze proste podzapytanie użyte w jednym miejscu.
WHERE id IN (SELECT id FROM ...)jest w porządku takie, jakie jest. - Zapytania krytyczne dla wydajności, dla których już sprawdzono, że wstawienie logiki w miejscu pomaga. SQLite zwykle traktuje CTE jako barierę optymalizacji mniej agresywnie niż niektóre inne bazy, ale na gorących ścieżkach warto to sprawdzić przez
EXPLAIN QUERY PLAN.
Przykład w praktyce
Składając wszystko razem: mały raport, który znajduje największe zamówienie każdego klienta i porównuje je z jego średnią:
Dwa CTE, każde robi jedną rzecz. Główny SELECT formatuje wynik. Zapytanie można czytać od góry do dołu i rozumieć każdy krok osobno, a o to właśnie chodzi w CTE.
Dalej: rekurencyjne CTE
Wszystko do tej pory było zwykłym CTE: nazwanym podzapytaniem obliczanym raz. SQLite obsługuje też WITH RECURSIVE, w którym CTE odwołuje się do samego siebie, aby przechodzić hierarchie, generować sekwencje lub przemierzać grafy. O tym na następnej stronie.
Najczęściej zadawane pytania
Czym jest CTE w SQLite?
Common Table Expression to nazwane podzapytanie umieszczone na początku SELECT, INSERT, UPDATE lub DELETE. Wprowadzasz je słowem kluczowym WITH, nadajesz mu nazwę, a potem używasz tej nazwy w głównym zapytaniu tak, jakby była tabelą. CTE sprawiają, że złożone zapytania są czytelne, bo pozwalają budować wynik krok po kroku.
Czym różni się CTE od podzapytania w SQLite?
Mogą dawać identyczne wyniki, bo CTE to w zasadzie podzapytanie wyciągnięte na zewnątrz i nazwane. Różnica to czytelność i ponowne użycie: do CTE można odwołać się kilka razy w tym samym zapytaniu, a jego nazwa dokumentuje, co przedstawia wynik pośredni. Do jednorazowych filtrów podzapytanie wystarczy, a przy logice wieloetapowej wygrywa CTE.
Czy w jednym zapytaniu SQLite może być kilka CTE?
Tak. Po pierwszym WITH oddzielaj kolejne CTE przecinkami, bez powtarzania WITH. Każde CTE może odwoływać się do zdefiniowanych przed nim, więc możesz zbudować potok nazwanych kroków. Główny SELECT następuje po ostatnim CTE.