Следующее расширение вашего тестового кода информативно:
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
OUTPUT:
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 раз и т. Д.
Существуют разные способы использования стабильности и неизменности с различными затратами и преимуществами.Чтобы обеспечить тот тип сохранения, который вы предлагаете, он должен сделать с select-списками, что он должен будет кэшировать результаты, а затем искать каждый аргумент (или список аргументов) в этом кэше, прежде чем либо возвращать кэшированный результат, либо вызывать функцию в кэше.-Мисс.Это будет дороже, чем вызов вашей функции, даже в случае высокого процента обращений к кешу (может быть 0% обращений к кешу, что означает, что эта «оптимизация» выполняла дополнительную работу, абсолютно не принося выгоды).Он может хранить, может быть, только последний параметр и результат, но, опять же, это может быть совершенно бесполезным.
Это особенно важно, учитывая, что стабильные и неизменные функции часто являются самыми легкими функциями.
С помощью whereТем не менее, в соответствии с предложением неизменность test_multi_calls1
позволяет PostgreSQL фактически реструктурировать запрос из простого значения заданного SQL:
Для каждой строки вычислить test_multi_calls1 (30) и, если результат равен 30продолжить обработку рассматриваемой строки
Для другого плана запроса полностью:
Вычислить test_multi_calls1 (30) и, если оно равно 30, продолжить с запросом, в противном случае вернутьнабор результатов с нулевой строкой без каких-либо дальнейших вычислений
Это тот тип использования, который PostgreSQL использует для STABLE и IMMUTABLE - не кэширование результатов, а переписывание запросов в различные запросы, которые болееэффективны, но дают те же результаты.
Обратите внимание, что тest_multi_calls1 (30) вызывается перед test_multi_calls2 (40) независимо от того, в каком порядке они появляются в предложении where.Это означает, что если в результате первого вызова не будет возвращено ни одной строки (замените = 30
на = 31
для проверки), функция volatile не будет вызываться вообще - опять же, независимо от того, какая сторона находится на and
.
Этот конкретный вид переписывания зависит от неизменности или стабильности.С where test_multi_calls1(30) != num
переписывание запросов будет происходить для неизменяемых, но не для просто стабильных функций.С where test_multi_calls1(num) != 30
этого не произойдет вообще (многократные вызовы), хотя возможны и другие варианты оптимизации:
Выражения, содержащие только функции STABLE и IMMUTABLE, могут использоваться при сканировании индекса.Выражения, содержащие функции VOLATILE, не могут.Количество вызовов может уменьшаться или не уменьшаться, но гораздо важнее то, что результаты вызовов будут использоваться гораздо более эффективно в остальной части запроса (это действительно имеет значение только для больших таблиц, но тогда это может привести к массовым изменениям).разница).
В целом, не думайте о категориях волатильности с точки зрения запоминания, а скорее с точки зрения предоставления планировщику запросов PostgreSQL возможности реструктурировать целые запросы способами, которые логически эквивалентны (те же результаты), но в значительной степениболее эффективный.