Postgresql 嵌套plpgsql函数中的计算比直接查询中的计算慢?
我有一个包含一些Postgresql 嵌套plpgsql函数中的计算比直接查询中的计算慢?,postgresql,plpgsql,sql-execution-plan,Postgresql,Plpgsql,Sql Execution Plan,我有一个包含一些文本和一些数字列的表格,例如: dimension_1, dimension_2, counter_1, counter_2 而不是执行查询 SELECT dimension_1, dimension_2, (counter_1, NULLIF(counter_2, 0)) as kpi from table order by kpi desc nulls last; 我想创建一个函数并执行以下操作: SELECT dimension_1, dimension_2, fun
文本
和一些数字
列的表格,例如:
dimension_1, dimension_2, counter_1, counter_2
而不是执行查询
SELECT dimension_1, dimension_2, (counter_1, NULLIF(counter_2, 0)) as kpi
from table order by kpi desc nulls last;
我想创建一个函数并执行以下操作:
SELECT dimension_1, dimension_2, func(counter_1, counter_2) as kpi
from table order by kpi desc nulls last;
我在Postgres中使用了以下实现:
CREATE FUNCTION kpi_latency_ext_msec(val1 numeric, val2 numeric)
RETURNS numeric AS $func$
BEGIN
RETURN ($1 / NULLIF($2, 0::numeric));
END; $func$
LANGUAGE PLPGSQL SECURITY DEFINER IMMUTABLE;
并获得期望的结果,但性能较慢
从解释分析
我得到:
第一次查询(带func):
Sort (cost=800.85..806.75 rows=2358 width=26) (actual time=5.534..5.710 rows=2358 loops=1)
Sort Key: (kpi_latency_ext_msec(external_tcp_handshake_latency_sum, external_tcp_handshake_latency_samples))
Sort Method: quicksort Memory: 281kB
-> Seq Scan on counters_by_cgi_rat (cost=0.00..668.76 rows=2358 width=26) (actual time=0.142..4.233 rows=2358 loops=1)
Filter: (("timestamp" >= '2018-05-10 00:00:00'::timestamp without time zone) AND ("timestamp" < '2018-05-13 00:00:00'::timestamp without time zone) AND (granularity = '1 day'::interval))
Planning time: 0.221 ms
Execution time: 5.881 ms
Sort (cost=223.14..229.04 rows=2358 width=26) (actual time=1.933..2.114 rows=2358 loops=1)
Sort Key: ((external_tcp_handshake_latency_sum / NULLIF(external_tcp_handshake_latency_samples, 0::numeric)))
Sort Method: quicksort Memory: 281kB
-> Seq Scan on counters_by_cgi_rat (cost=0.00..91.06 rows=2358 width=26) (actual time=0.010..1.190 rows=2358 loops=1)
Filter: (("timestamp" >= '2018-05-10 00:00:00'::timestamp without time zone) AND ("timestamp" < '2018-05-13 00:00:00'::timestamp without time zone) AND (granularity = '1 day'::interval))
Planning time: 0.139 ms
Execution time: 2.279 ms
Seq Scan on table (cost=0.00..91.06 rows=2358 width=26) (actual time=0.016..1.223 rows=2358 loops=1)
Seq Scan on table (cost=0.00..668.76 rows=2358 width=26) (actual time=0.123..3.518 rows=2358 loops=1)
与func:
Sort (cost=800.85..806.75 rows=2358 width=26) (actual time=5.534..5.710 rows=2358 loops=1)
Sort Key: (kpi_latency_ext_msec(external_tcp_handshake_latency_sum, external_tcp_handshake_latency_samples))
Sort Method: quicksort Memory: 281kB
-> Seq Scan on counters_by_cgi_rat (cost=0.00..668.76 rows=2358 width=26) (actual time=0.142..4.233 rows=2358 loops=1)
Filter: (("timestamp" >= '2018-05-10 00:00:00'::timestamp without time zone) AND ("timestamp" < '2018-05-13 00:00:00'::timestamp without time zone) AND (granularity = '1 day'::interval))
Planning time: 0.221 ms
Execution time: 5.881 ms
Sort (cost=223.14..229.04 rows=2358 width=26) (actual time=1.933..2.114 rows=2358 loops=1)
Sort Key: ((external_tcp_handshake_latency_sum / NULLIF(external_tcp_handshake_latency_samples, 0::numeric)))
Sort Method: quicksort Memory: 281kB
-> Seq Scan on counters_by_cgi_rat (cost=0.00..91.06 rows=2358 width=26) (actual time=0.010..1.190 rows=2358 loops=1)
Filter: (("timestamp" >= '2018-05-10 00:00:00'::timestamp without time zone) AND ("timestamp" < '2018-05-13 00:00:00'::timestamp without time zone) AND (granularity = '1 day'::interval))
Planning time: 0.139 ms
Execution time: 2.279 ms
Seq Scan on table (cost=0.00..91.06 rows=2358 width=26) (actual time=0.016..1.223 rows=2358 loops=1)
Seq Scan on table (cost=0.00..668.76 rows=2358 width=26) (actual time=0.123..3.518 rows=2358 loops=1)
功能的结果无安全定义
Seq Scan on counters_by_cgi_rat (cost=0.00..668.76 rows=2358 width=26)
(actual time=0.035..3.718 rows=2358 loops=1)
Filter: (("timestamp" >= '2018-05-10 00:00:00'::timestamp without time zone)
AND ("timestamp" < '2018-05-13 00:00:00'::timestamp without time zone)
AND (granularity = '1 day'::interval))
Planning time: 0.086 ms
Execution time: 3.923 ms
使用上述功能获得的最佳结果(甚至比普通查询更快)毒药镖是
安全定义者
。声明的函数安全定义器
不能内联-如果我没有弄错的话,可以强制执行上下文切换。这会使它们的价格大大提高。在本例中,确实不需要安全定义程序。简单计算不需要不同的权限。(可能您的实际用例不同。)
而且也不需要PL/pgSQL只有SQL函数可以内联-如果满足一些附加的先决条件
由于所有使用的函数都是不可变的
,因此应该声明函数不可变
。(默认函数VOLATILE为VOLATILE
)您已经相应地更新了问题。这允许表达式索引,并有助于防止在某些情况下重复计算。但是它从不帮助函数内联。相反:它强加了更多的先决条件(在本例中满足这些先决条件)。引用(撰写本文时的2016年最新更新):
如果函数声明为不可变,则表达式不能
调用任何不可变的函数或运算符
引述:
这里的基本要点是,标记为volatile的函数可以
扩展到包含的函数,即使它们是不可变的;但是
另一方面,表示潜在的语义变化,因此
计划者不会这么做的
解决方案
尝试不使用安全定义程序
:
CREATE FUNCTION kpi_latency_ext_msec(val1 numeric, val2 numeric)
RETURNS numeric AS
$func$
BEGIN
RETURN $1 / NULLIF($2, numeric '0');
END
$func$ LANGUAGE plpgsql IMMUTABLE;
应该已经快多了
或者从根本上简化为SQL函数:
CREATE FUNCTION f_div0_sql_nullif(val1 numeric, val2 numeric)
RETURNS numeric LANGUAGE sql IMMUTABLE AS
$$SELECT $1 / NULLIF($2, numeric '0')$$;
更快了吗
相关的:
IF
和CASE
表达式,但是在我运行了大量测试之后,显示NULLIF
稍微快一点。因此,我相应地简化为原始的NULLIF
变体
主要的要点仍然是没有安全定义符
,SQL
和不可变
DBFIDLE-第10页dbfiddle-pg 9.4我可能会将其简化为:
选择$1/nullif($2,0)
-在这种情况下,案例会更快吗?@a_horse_没有名字:我运行了一些测试,发现你是对的:nullif
似乎稍微快一点。添加了上述结果。使用nullif并将函数定义为稳定函数比普通函数具有更好的性能query@dimitrislepipas:稳定的
和不可变的
在您的情况下应该会导致此函数具有相同的性能。(我实际测试过。)但是不可变
通常更可取,如果适用的话,因为它还有其他好处。它可以用于表达式索引等。