我有一个非常简单的查询,它不比以下复杂得多:
select *
from table_name
where id = 1234
......运行时间不到50毫秒。
接受该查询并将其放入函数中:
CREATE OR REPLACE FUNCTION pie(id_param integer)
RETURNS SETOF record AS
$BODY$
BEGIN
RETURN QUERY SELECT *
FROM table_name
where id = id_param;
END
$BODY$
LANGUAGE plpgsql STABLE;
执行此功能select * from pie(123);
需要22秒。
如果我用一个整数来代替id_param,那么该函数的执行时间不到50毫秒。
为什么我在where语句中使用参数会导致我的函数运行缓慢?
编辑以添加具体示例:
CREATE TYPE test_type AS (gid integer, geocode character varying(9))
CREATE OR REPLACE FUNCTION geocode_route_by_geocode(geocode_param character)
RETURNS SETOF test_type AS
$BODY$
BEGIN
RETURN QUERY EXECUTE
'SELECT gs.geo_shape_id AS gid,
gs.geocode
FROM geo_shapes gs
WHERE geocode = $1
AND geo_type = 1
GROUP BY geography, gid, geocode' USING geocode_param;
END;
$BODY$
LANGUAGE plpgsql STABLE;
ALTER FUNCTION geocode_carrier_route_by_geocode(character)
OWNER TO root;
--Runs in 20 seconds
select * from geocode_route_by_geocode('999xyz');
--Runs in 10 milliseconds
SELECT gs.geo_shape_id AS gid,
gs.geocode
FROM geo_shapes gs
WHERE geocode = '9999xyz'
AND geo_type = 1
GROUP BY geography, gid, geocode
答案 0 :(得分:7)
有一个重大改进,我引用了release notes here:
允许规划人员为特定参数生成自定义计划 即使使用准备好的语句(Tom Lane)
也是如此过去,准备好的声明总是有一个“通用”计划 用于所有参数值,这通常很多 不如用于包含的非预备陈述的计划 显式常量值。现在,计划者试图生成自定义 计划特定的参数值。只会使用通用计划 经过多次证明定制计划无法提供任何好处。这个 改变应该消除以前看到的绩效惩罚 使用准备好的陈述(包括非动态陈述) PL / pgSQL的)。
plpgsql函数与PREPARE
语句具有类似的效果:解析查询并缓存查询计划。
优点是每次通话都会节省一些开销 缺点是查询计划没有针对调用它的特定参数值进行优化。
对于具有偶数数据分布的表的查询,这通常没有问题,并且PL / pgSQL函数的执行速度比原始SQL查询或SQL函数快一些。但是,如果您的查询可以根据WHERE
子句中的实际值使用某些索引,或者更一般地,为特定值选择更好的查询计划,则最终可能会得到次优查询计划。尝试使用SQL函数或使用EXECUTE
的动态SQL强制为每个调用重新计划查询。可能看起来像这样:
CREATE OR REPLACE FUNCTION pie(id_param integer)
RETURNS SETOF record AS
$BODY$
BEGIN
RETURN QUERY EXECUTE
'SELECT *
FROM table_name
where id = $1'
USING id_param;
END
$BODY$
LANGUAGE plpgsql STABLE;
评论后编辑:
如果此变体不会改变执行时间,则必须有其他因素可能已经错过或未提及。不同数据库?不同的参数值?您必须发布更多详细信息。
我添加引用from the manual来支持我的上述陈述:
带有简单常量命令字符串和一些USING的EXECUTE 如上面第一个例子中的参数在功能上是等价的 直接在PL / pgSQL中编写命令并允许 将PL / pgSQL变量替换为自动发生。该 重要的区别是EXECUTE会重新计划每个命令 执行,生成特定于当前参数的计划 值;而PL / pgSQL通常会创建一个通用计划并对其进行缓存 重复使用。在最佳计划强烈依赖于的情况下 参数值,EXECUTE可以明显更快;而当时 计划对参数值不敏感,重新计划将是一个 浪费。