Следното разширение на вашия тестов код е информативно:
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
ИЗХОД:
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
Тук можем да видим, че докато в списъка за избор неизменната функция е извикана многократно, в клаузата where е извикана веднъж, докато volatile е извикана три пъти.
Важното не е, че PostgreSQL ще извика само STABLE
или IMMUTABLE
функционира веднъж със същите данни - вашият пример ясно показва, че това не е така - това е, че може обадете се само веднъж. Или може би ще го извика два пъти, когато ще трябва да извика променлива версия 50 пъти и т.н.
Има различни начини, по които стабилността и неизменността могат да се възползват, с различни разходи и ползи. За да осигури вида спестяване, който предполагате, че трябва да направи със списъци за избор, ще трябва да кешира резултатите и след това да търси всеки аргумент (или списък с аргументи) в този кеш, преди или да върне кеширания резултат, или да извика функция в кеш -мис. Това би било по-скъпо, отколкото да извикате функцията си, дори в случай, когато имаше висок процент на кеш попадения (може да има 0% кеш попадения, което означава, че тази „оптимизация“ е свършила допълнителна работа без абсолютно никаква печалба). Може да съхранява може би само последния параметър и резултат, но отново това може да е напълно безполезно.
Това е особено важно, като се има предвид, че стабилните и неизменни функции често са най-леките функции.
С клаузата where обаче, неизменността на test_multi_calls1
позволява на PostgreSQL действително да преструктурира заявката от обикновения смисъл на дадения SQL:
Към изцяло различен план на заявка:
Това е начинът, по който PostgreSQL използва STABLE и IMMUTABLE - не кеширане на резултати, а пренаписване на заявки в различни заявки, които са по-ефективни, но дават същите резултати.
Обърнете внимание също, че test_multi_calls1(30) се извиква преди test_multi_calls2(40) без значение в какъв ред се появяват в клаузата where. Това означава, че ако първото извикване не доведе до връщане на редове (заменете = 30
с = 31
за тестване), тогава променливата функция изобщо няма да бъде извикана - отново независимо кой е от коя страна на and
.
Този конкретен вид пренаписване зависи от неизменността или стабилността. С where test_multi_calls1(30) != num
пренаписването на заявката ще се случи за неизменни, но не и за просто стабилни функции. С where test_multi_calls1(num) != 30
това изобщо няма да се случи (множество обаждания), въпреки че има други възможни оптимизации:
Изрази, съдържащи само STABLE и IMMUTABLE функции, могат да се използват с индексни сканирания. Изрази, съдържащи функции VOLATILE, не могат. Броят на извикванията може да намалее или да не намалее, но много по-важното е, че резултатите от извикванията след това ще бъдат използвани по много по-ефективен начин в останалата част от заявката (наистина има значение само при големи таблици, но тогава може да направи огромен разлика).
Като цяло, не мислете за категориите на променливостта от гледна точка на мемоизация, а по-скоро от гледна точка на предоставяне на възможности за планиране на заявки на PostgreSQL да преструктурира цели заявки по начини, които са логически еквивалентни (същите резултати), но много по-ефективни.