Термин «встраивание» имеет другое значение в Postgres. Обычно это относится к language sql
функциям, которые полностью заменяются вложенным запросом при использовании внутри другого запроса, например, эта функция
create or replace function customers_in_zip(p_zip_code varchar(5))
returns setof customers
as
$$
select *
from customers
where zip_code = p_zip_code;
$$
language sql;
используется так:
select *
from orders o
join customers_in_zip('42') c on o.customer_id = c.id;
будет расширен оптимизатором до:
select *
from orders o
join customers c on o.customer_id = c.id and c.zip_code = '42';
Этот тип встраивания можно увидеть при генерации плана выполнения с использованием explain (analyze)
. Для этого функция должна быть помечена как immutable
или stable
например. если функция может быть «встроенной», план выглядит примерно так:
Nested Loop (cost=2.39..200.79 rows=79 width=52) (actual time=0.021..0.165 rows=115 loops=1)
-> Bitmap Heap Scan on public.customers (cost=1.97..20.71 rows=13 width=28) (actual time=0.014..0.023 rows=15 loops=1)
Recheck Cond: ((customers.zip_code)::text = '80807'::text)
-> Bitmap Index Scan on customers_zip_code_idx (cost=0.00..1.96 rows=13 width=0) (actual time=0.010..0.010 rows=15 loops=1)
Index Cond: ((customers.zip_code)::text = '80807'::text)
-> Index Scan using idx_orders_cust_id on public.orders o (cost=0.42..13.84 rows=8 width=24) (actual time=0.003..0.008 rows=8 loops=15)
Index Cond: (o.customer_id = customers.id)
Как видите, ссылка на функцию отсутствует (план запроса без функции выглядит примерно так же).
Если функция не встроенная (например, потому что она не была объявлена stable
или потому что это функция PL / pgSQL, а не функция SQL), план будет выглядеть примерно так:
Nested Loop (cost=0.68..139.94 rows=77 width=110) (actual time=0.710..0.862 rows=115 loops=1)
-> Function Scan on public.customers_in_zip c (cost=0.25..0.26 rows=10 width=86) (actual time=0.696..0.697 rows=15 loops=1)
Function Call: customers_in_zip('42'::character varying)
Buffers: shared hit=18
-> Index Scan using idx_orders_cust_id on public.orders o (cost=0.42..13.96 rows=8 width=24) (actual time=0.004..0.009 rows=8 loops=15)
Output: o.id, o.customer_id, o.order_date, o.amount, o.sales_person_id
Index Cond: (o.customer_id = c.id)
Из вашего описания кажется, что вы не имеете в виду такого рода "встраивание", а скорее, вызывается ли скалярная функция только один раз, если она не зависит от значений, взятых из строки, например ::
select col1, some_function(), col2
from some_table;
Если some_function()
объявлено immutable
, оно будет вызвано только один раз.
Цитата из руководства
IMMUTABLE указывает, что функция не может изменять базу данных и всегда возвращает один и тот же результат, если даны одинаковые значения аргумента; [...] Если указана эта опция, любой вызов функции с полностью константными аргументами может быть немедленно заменен значением функции.
Это не то, что вы можете видеть непосредственно в плане выполнения, но следующее продемонстрирует это:
create function expensive_scalar(p_some_input integer)
returns integer
as
$$
begin
perform pg_sleep(10);
return p_some_input * 2;
end;
$$
language plpgsql
<b>IMMUTABLE</b>;
perform pg_sleep(10);
заставляет функцию занять 10 секунд для выполнения. Следующий запрос будет вызывать эту функцию сто раз:
select i, expensive_scalar(2)
from generate_series(1,100) i;
Но выполнение занимает чуть более 10 секунд, что ясно показывает, что функция вызывается только один раз.
Насколько я знаю, Postgres также будет кэшировать результаты для функций, помеченных как stable
во время выполнения одного оператора (для тех же значений ввода).
Это немного сложнее показать, хотя. Обычно вы можете сделать это, поместив в функцию raise notice
операторов (эквивалентных Postgres print
) и посмотрите, как часто они печатаются.