Poniższe rozszerzenie kodu testowego ma charakter informacyjny:
CREATE OR REPLACE FUNCTION test_multi_calls1(one integer)
RETURNS integer
AS $BODY$
BEGIN
RAISE NOTICE 'Immutable called with %', one;
RETURN one;
END;
$BODY$ LANGUAGE plpgsql IMMUTABLE;
CREATE OR REPLACE FUNCTION test_multi_calls2(one integer)
RETURNS integer
AS $BODY$
BEGIN
RAISE NOTICE 'Volatile called with %', one;
RETURN one;
END;
$BODY$ LANGUAGE plpgsql VOLATILE;
WITH data AS
(
SELECT 10 AS num
UNION ALL SELECT 10
UNION ALL SELECT 20
)
SELECT test_multi_calls1(num)
FROM data
where test_multi_calls2(40) = 40
and test_multi_calls1(30) = 30
WYJŚCIE:
NOTICE: Immutable called with 30
NOTICE: Volatile called with 40
NOTICE: Immutable called with 10
NOTICE: Volatile called with 40
NOTICE: Immutable called with 10
NOTICE: Volatile called with 40
NOTICE: Immutable called with 20
Tutaj widzimy, że podczas gdy na liście wyboru funkcja niezmienna była wywoływana wiele razy, w klauzuli where została wywołana raz, podczas gdy funkcja volatile została wywołana trzykrotnie.
Ważne nie jest to, że PostgreSQL wywoła tylko STABLE
lub IMMUTABLE
funkcjonować raz z tymi samymi danymi – Twój przykład wyraźnie pokazuje, że tak nie jest – po prostu może nazwij to tylko raz. A może wywoła go dwukrotnie, gdy będzie musiał wywołać wersję lotną 50 razy i tak dalej.
Istnieją różne sposoby wykorzystania stabilności i niezmienności, z różnymi kosztami i korzyściami. Aby zapewnić rodzaj zapisywania, który sugerujesz, powinien wykonać z listami wyboru, musiałby buforować wyniki, a następnie wyszukać każdy argument (lub listę argumentów) w tej pamięci podręcznej przed zwróceniem wyniku z pamięci podręcznej lub wywołaniem funkcji w pamięci podręcznej -tęsknić. Byłoby to droższe niż wywołanie funkcji, nawet w przypadku wysokiego odsetka trafień w pamięci podręcznej (może być 0% trafień w pamięci podręcznej, co oznacza, że ta „optymalizacja” wykonała dodatkową pracę bez żadnego zysku). Może przechowywać może tylko ostatni parametr i wynik, ale znowu może to być całkowicie bezużyteczne.
Jest to szczególnie ważne, biorąc pod uwagę, że stabilne i niezmienne funkcje są często najlżejszymi funkcjami.
Jednak z klauzulą where niezmienność test_multi_calls1
pozwala PostgreSQL na rzeczywistą restrukturyzację zapytania z prostego znaczenia podanego SQL:
Do zupełnie innego planu zapytań:
Jest to rodzaj zastosowania, jakie PostgreSQL wykorzystuje ze STABLE i IMMUTABLE - nie buforowanie wyników, ale przepisywanie zapytań na różne zapytania, które są bardziej wydajne, ale dają te same wyniki.
Zauważ również, że test_multi_calls1(30) jest wywoływany przed test_multi_calls2(40) bez względu na kolejność ich występowania w klauzuli where. Oznacza to, że jeśli pierwsze wywołanie nie spowoduje zwrócenia żadnych wierszy (zastąp = 30
z = 31
do przetestowania), wtedy funkcja volatile nie zostanie w ogóle wywołana - ponownie, niezależnie od tego, która jest po której stronie and
.
Ten szczególny rodzaj przepisywania zależy od niezmienności lub stabilności. Z where test_multi_calls1(30) != num
Przepisywanie zapytań nastąpi dla funkcji niezmiennych, ale nie tylko dla funkcji stabilnych. Z where test_multi_calls1(num) != 30
to się w ogóle nie stanie (wiele połączeń), chociaż możliwe są inne optymalizacje:
Wyrażenia zawierające tylko funkcje STABILNE i NIEZMIENNE mogą być używane ze skanowaniem indeksu. Wyrażenia zawierające funkcje VOLATILE nie mogą. Liczba wywołań może się zmniejszyć lub nie, ale o wiele ważniejsze jest to, że wyniki wywołań będą wtedy wykorzystywane w znacznie wydajniejszy sposób w pozostałej części zapytania (istnieje tylko w przypadku dużych tabel, ale wtedy może to spowodować ogromne różnica).
Podsumowując, nie myśl o kategoriach zmienności w kategoriach zapamiętywania, ale raczej w kategoriach dawania planerowi zapytań PostgreSQL możliwości restrukturyzowania całych zapytań w sposób, który jest logicznie równoważny (te same wyniki), ale o wiele bardziej wydajny.