术语“内联”在 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 函数),那么计划看起来像这样:
嵌套循环(成本=0.68..139.94 行=77 宽度=110)(实际时间=0.710..0.862 行=115 循环=1)
-> public.customers_in_zip c 上的函数扫描(成本=0.25..0.26 行=10 宽度=86)(实际时间=0.696..0.697 行=15 循环=1)
函数调用:customers_in_zip('42'::character varying)
缓冲区:共享命中=18
-> 在 public.orders o 上使用 idx_orders_cust_id 进行索引扫描(成本=0.42..13.96 行=8 宽度=24)(实际时间=0.004..0.009 行=8 循环=15)
输出:o.id、o.customer_id、o.order_date、o.amount、o.sales_person_id
指数条件:(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
IMMUTABLE;
使perform pg_sleep(10);
函数需要 10 秒才能执行。以下查询将调用该函数一百次:
select i, expensive_scalar(2)
from generate_series(1,100) i;
但是执行只用了10多秒,这清楚地表明该函数只被调用了一次。
据我所知,Postgres 还将缓存标记为stable
在执行单个语句期间的函数的结果(对于相同的输入值)。
不过,这有点难以展示。通常,您可以通过将raise notice
语句(Postgres 的等效于print
)放入函数中并查看它们的打印频率来做到这一点。