使用输入变量时,Postgres功能要慢得多

str*_*g13 2 postgresql performance function plpgsql postgresql-8.3

我在Postgres 8.3.5中有一个函数,它从多个表中选择数据并将结果转储到一个表中:

create or replace function test_function_2(startdate timestamp, enddate timestamp)
returns void as $$
begin
     delete from cl_final_report;

     INSERT INTO cl_final_report
     SELECT 
         b.batchkey AS batchnumber, 
         pv.productkey, 
         p.name AS productname, 
         avg(r.value) AS avgchemlean, 
         sum(r.auxvalue) AS totalweight, 
         max(o.time) AS timecompleted
     FROM result r
     LEFT JOIN physicalvalue pv ON r.physicalvaluekey = pv.physicalvaluekey
     LEFT JOIN product p ON pv.productkey = p.productkey
     LEFT JOIN object o ON r.objectkey = o.objectkey
     LEFT JOIN batch b ON o.batchkey = b.batchkey
     WHERE pv.name = 'CL'::text AND
         and o.time between startdate and enddate
     GROUP BY b.batchkey, pv.productkey, p.name
end
$$ language plpgsql;
Run Code Online (Sandbox Code Playgroud)

使用PgAdmin并执行此命令,此函数需要113秒才能完成:

select test_function_2('05/02/2013', '05/03/2013')
Run Code Online (Sandbox Code Playgroud)

但是,如果我用这样的文字替换函数中的输入变量:

create or replace function test_function_2(startdate timestamp, enddate timestamp)
returns void as $$
begin
     delete from cl_final_report;

     INSERT INTO cl_final_report
     SELECT 
         b.batchkey AS batchnumber, 
         pv.productkey, 
         p.name AS productname, 
         avg(r.value) AS avgchemlean, 
         sum(r.auxvalue) AS totalweight, 
         max(o.time) AS timecompleted
     FROM result r
     LEFT JOIN physicalvalue pv ON r.physicalvaluekey = pv.physicalvaluekey
     LEFT JOIN product p ON pv.productkey = p.productkey
     LEFT JOIN object o ON r.objectkey = o.objectkey
     LEFT JOIN batch b ON o.batchkey = b.batchkey
     WHERE pv.name = 'CL'::text AND
         and o.time between '05/02/2013' and '05/03/2013'
     GROUP BY b.batchkey, pv.productkey, p.name
end
$$ language plpgsql;
Run Code Online (Sandbox Code Playgroud)

该功能在不到5秒的时间内执行.

我是Postgres的新手,所以我可能会缺少一些东西,但我似乎无法在任何地方找到答案.

Erw*_*ter 7

@ AH的解释对于PostgreSQL 9.1或更早版本是准确的.因此它适用于使用过时版本8.3的OP.

但是,PostgreSQL 9.2在这方面带来了实质性的更新.关于何时重新计划,PL/pgSQL函数变得更加智能.我在这里引用9.2发行说明

E.5.3.1.3.优化

即使使用预准备语句,也允许计划程序为特定参数值生成自定义计划(Tom Lane)

过去,准备好的语句总是有一个用于所有参数值的"通用"计划,这通常远远低于用于包含显式常量值的非预处理语句的计划.现在,计划程序尝试为特定参数值生成自定义计划.只有在反复证明自定义计划不能提供任何好处之后,才能使用通用计划.此更改应消除以前使用准备语句(包括PL/pgSQL中的非动态语句)所看到的性能损失.

大胆强调我的.

Ergo:OP的一个解决方案是升级到PostgreSQL 9.2+,一切都应该自动正常工作.


A.H*_*.H. 6

查询计划器/优化器可以在手头有常量时计算出更好的计划.

当使用没有常量规划器必须产生一个计划这是可接受的所有的可能的值startdateenddate.如果这两个值之间的差异非常大,则必须获取表的大部分内容.在这种情况下,在大多数情况下不使用indizes,因为随机访问成本高于线性读取.

但是当有常量时,规划器可以根据收集的统计数据计算,查询只会触及表的一小部分,因此索引可能会更快.

这是PostgreSQL查询规划器的常见问题.该手册包含PREPARE部分中的一些提示(PREPARE由pl/pgsql内部使用):

在某些情况下,为预准备语句生成的查询计划将不如在语句已正确提交和执行时选择的查询计划.这是因为在计划语句并且计划程序尝试确定最佳查询计划时,语句中指定的任何参数的实际值都不可用.PostgreSQL收集有关表中数据分布的统计信息,并且可以在语句中使用常量值来猜测执行语句的可能结果.由于在计划带参数的预准备语句时此数据不可用,因此所选计划可能不是最理想的.要检查PostgreSQL为预准备语句选择的查询计划,请使用EXPLAIN.