我正在尝试找出与参数化SQL函数的性能不佳相关的问题。该文档对PL / PGSQL函数中的执行计划相对比较清楚-查询计划者计划前5次执行,然后可以在同一会话后将执行计划进行缓存。
但是,我一直无法找到与SQL函数相关的类似文档,并且通过一些调查,似乎查询计划者总是使用通用计划。
这是一个简单的例子:
-- Postgres 11.1
-- Test table and some data
create table test (a int);
insert into test select 1 from generate_series(1,1000000);
insert into test values (2);
create index on test(a);
analyze test;
-- A SQL Function
create function test_f(val int, cnt out bigint) AS $$
SELECT count(*) FROM test where a = val;
$$ LANGUAGE SQL;
-- A similar PL/PGSQL Function
create function test_f_plpgsql (val int, cnt out bigint) AS $$
BEGIN
SELECT count(*) FROM test where a = val INTO cnt;
END
$$ LANGUAGE PLPGSQL;
-- Show plans
LOAD 'auto_explain';
SET auto_explain.log_analyze = on;
SET client_min_messages = log;
SET auto_explain.log_nested_statements = on;
SET auto_explain.log_min_duration = 0;
PL / PGSQL函数使用非通用计划,并且知道使用仅索引扫描。
select * from test_f_plpgsql(2);
LOG: duration: 0.326 ms plan:
Query Text: SELECT count(*) FROM test where a = val
Aggregate (cost=4.45..4.46 rows=1 width=8) (actual time=0.180..0.202 rows=1 loops=1)
-> Index Only Scan using test_a_idx on test (cost=0.42..4.44 rows=1 width=0) (actual time=0.096..0.135 rows=1 loops=1)
Index Cond: (a = 2)
Heap Fetches: 1
LOG: duration: 1.250 ms plan:
Query Text: select * from test_f_plpgsql(2);
Function Scan on test_f_plpgsql (cost=0.25..0.26 rows=1 width=8) (actual time=1.116..1.152 rows=1 loops=1)
cnt
-----
1
(1 row)
另一方面,SQL函数使用通用计划,因此很难选择全表扫描。
select * from test_f(2);
LOG: duration: 18.716 ms plan:
Query Text:
SELECT count(*) FROM test where a = val;
Partial Aggregate (cost=10675.01..10675.02 rows=1 width=8) (actual time=18.639..18.665 rows=1 loops=1)
-> Parallel Seq Scan on test (cost=0.00..9633.34 rows=416667 width=0) (actual time=18.621..18.628 rows=0 loops=1)
Filter: (a = $1)
Rows Removed by Filter: 273008
LOG: duration: 28.304 ms plan:
Query Text:
SELECT count(*) FROM test where a = val;
Partial Aggregate (cost=10675.01..10675.02 rows=1 width=8) (actual time=28.234..28.248 rows=1 loops=1)
-> Parallel Seq Scan on test (cost=0.00..9633.34 rows=416667 width=0) (actual time=28.199..28.208 rows=1 loops=1)
Filter: (a = $1)
Rows Removed by Filter: 129222
LOG: duration: 45.913 ms plan:
Query Text:
SELECT count(*) FROM test where a = val;
Finalize Aggregate (cost=11675.22..11675.23 rows=1 width=8) (actual time=42.370..42.377 rows=1 loops=1)
-> Gather (cost=11675.01..11675.22 rows=2 width=8) (actual time=42.288..45.787 rows=3 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Partial Aggregate (cost=10675.01..10675.02 rows=1 width=8) (actual time=29.579..29.597 rows=1 loops=3)
-> Parallel Seq Scan on test (cost=0.00..9633.34 rows=416667 width=0) (actual time=29.553..29.560 rows=0 loops=3)
Filter: (a = $1)
Rows Removed by Filter: 333333
LOG: duration: 47.128 ms plan:
Query Text: select * from test_f(2);
Function Scan on test_f (cost=0.25..0.26 rows=1 width=8) (actual time=47.058..47.073 rows=1 loops=1)
cnt
-----
1
(1 row)
是否有一种方法可以强制SQL函数使用非泛型计划?