Obliczanie kumulowanych wyników i sum bieżących tradycyjnie w SQL było realizowane poprzez funkcje okienne (na przykład, SUM() OVER(ORDER BY ...)), jednak w wczesnych lub uproszczonych wersjach DBMS dostępne są jedynie podzapytania i grupowania. Historycznie architekci baz danych poszukiwali obejść do momentu pojawienia się standardu SQL:2003 z obsługą funkcji okiennych.
Problem — przy braku funkcji okiennych dla każdego wiersza konieczne jest jawne obliczenie sumy wszystkich poprzednich wartości, co prowadzi do złożoności O(N^2) w przypadku dużych zbiorów danych, jeśli nie zastosuje się sprytnych rozwiązań.
Rozwiązanie:
Zazwyczaj używa się skorelowanych podzapytań lub tymczasowych tabel z aktualizacją wartości:
Przykład kodu:
-- Kumulowana suma przez skorelowane zapytanie SELECT t1.id, t1.amount, ( SELECT SUM(t2.amount) FROM transactions t2 WHERE t2.id <= t1.id ) AS running_total FROM transactions t1 ORDER BY t1.id; -- Przez tymczasową tabelę z ręcznym aktualizowaniem wartości CREATE TEMPORARY TABLE temp_running (id INT, amount INT, running_total INT); -- Przechodzimy przez wiersze używając zewnętrznego kodu (na przykład, pl/pgsql), kolejno dodając sumę
Kluczowe cechy:
Czy gwarantuje posortowanie ORDER BY w skorelowanym podzapytaniu?
Nie — zapytanie podrzędne niekoniecznie wpływa na sam wynik. Sortowanie końcowego zbioru jest zawsze określane zewnętrznie w głównym zapytaniu: wynik zależy tylko od filtracji według WHERE.
Czy można równolegle obliczać kumulowaną sumę w tym podejściu?
Nie — kolejność jest bardzo ważna, szczególnie przy obliczeniach w zależności od poprzednich wierszy, przez co proste równoleglenie jest niemożliwe w zwykłym SQL.
Dlaczego skorelowane podzapytanie jest tak wolne przy dużej liczbie wierszy?
Ponieważ dla każdego wiersza na nowo obliczana jest suma dla zestawu poprzednich wierszy. To prowadzi do O(N^2) operacji. Na próbce 100 tys. wierszy to już może zająć minuty, a nawet godziny.
Analityk obliczył dzienną kumulowaną przychód według daty przez skorelowane podzapytanie, a w tabeli okresowo pojawiały się usunięte id (dziury). Końcowa suma miała skokowe spadki i zależała nie od daty, ale od porządku id.
Plusy:
Minusy:
Inżynier przeniósł przetwarzanie kumulowanej sumy do skryptu ETL (Python/pandas), a następnie załadował końcowe wartości do osobnej tabeli, synchronizując tylko nowości. Wyniki zawsze są zgodne z datą, kod działa szybko i z milionami rekordów.
Plusy:
Minusy: