为了获得最佳的优化结果,您应该使用对他们有效的最严格的波动率类别标记您的函数。
但是,我似乎有一个例子,并非如此,我想了解发生了什么。 (背景:我正在运行postgres 9.2)
我经常需要将表示为整数秒数的时间转换为日期。我已经编写了一个函数来执行此操作:
CREATE OR REPLACE FUNCTION
to_datestamp(time_int double precision) RETURNS date AS $$
SELECT date_trunc('day', to_timestamp($1))::date;
$$ LANGUAGE SQL;
让我们将性能与其他相同的函数进行比较,将波动率设置为IMMUTABLE和STABLE:
CREATE OR REPLACE FUNCTION
to_datestamp_immutable(time_int double precision) RETURNS date AS $$
SELECT date_trunc('day', to_timestamp($1))::date;
$$ LANGUAGE SQL IMMUTABLE;
CREATE OR REPLACE FUNCTION
to_datestamp_stable(time_int double precision) RETURNS date AS $$
SELECT date_trunc('day', to_timestamp($1))::date;
$$ LANGUAGE SQL STABLE;
为了测试这一点,我将创建一个10 ^ 6个随机整数的表,对应于2010-01-01和2015-01-01之间的时间
CREATE TEMPORARY TABLE random_times AS
SELECT 1262304000 + round(random() * 157766400) AS time_int
FROM generate_series(1, 1000000) x;
最后,我会在这张桌子上调用两个函数;在我的特定盒子上,原始需要约6秒,不可变版本需要~33秒,而稳定版本需要约6秒。
EXPLAIN ANALYZE SELECT to_datestamp(time_int) FROM random_times;
Seq Scan on random_times (cost=0.00..20996.62 rows=946950 width=8)
(actual time=0.150..5493.722 rows=1000000 loops=1)
Total runtime: 6258.827 ms
EXPLAIN ANALYZE SELECT to_datestamp_immutable(time_int) FROM random_times;
Seq Scan on random_times (cost=0.00..250632.00 rows=946950 width=8)
(actual time=0.211..32209.964 rows=1000000 loops=1)
Total runtime: 33060.918 ms
EXPLAIN ANALYZE SELECT to_datestamp_stable(time_int) FROM random_times;
Seq Scan on random_times (cost=0.00..20996.62 rows=946950 width=8)
(actual time=0.086..5295.608 rows=1000000 loops=1)
Total runtime: 6063.498 ms
这里发生了什么?例如,postgres花时间缓存结果,因为传递给函数的参数不太可能重复,因此实际上没有用处?
(我正在运行postgres 9.2。)
谢谢!
更新
感谢Craig Ringer,pgsql-performance mailing list已对此进行了讨论。亮点:
[耸耸肩......]使用IMMUTABLE来解释函数的可变性 (在这种情况下,date_trunc)是个坏主意。它可能导致错误 答案,不要介意性能问题。在这个特殊情况下,我 想象性能问题来自抑制选项 内联函数体......但你应该更担心 在其他情况下,你是否没有得到平坦的虚假答案。
如果我理解,使用过的IMMUTABLE标志会禁用内联。你看到的是,是 SQL eval溢出。我的规则是 - 尽可能不在SQL函数中使用标志。
答案 0 :(得分:2)
问题是to_timestamp
会返回带时区的时间戳。如果to_timestamp
函数被替换为没有时区的“手动”计算,则性能没有差异
create or replace function to_datestamp_stable(
time_int double precision
) returns date as $$
select date_trunc('day', timestamp 'epoch' + $1 * interval '1 second')::date;
$$ language sql stable;
explain analyze
select to_datestamp_stable(a)
from generate_series(1, 1000000) s (a);
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------
Function Scan on generate_series s (cost=0.00..22.50 rows=1000 width=4) (actual time=96.962..433.562 rows=1000000 loops=1)
Total runtime: 459.531 ms
create or replace function to_datestamp_immutable(
time_int double precision
) returns date as $$
select date_trunc('day', timestamp 'epoch' + $1 * interval '1 second')::date;
$$ language sql immutable;
explain analyze
select to_datestamp_immutable(a)
from generate_series(1, 1000000) s (a);
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------
Function Scan on generate_series s (cost=0.00..22.50 rows=1000 width=4) (actual time=94.188..433.492 rows=1000000 loops=1)
Total runtime: 459.434 ms
使用to_timestamp
create or replace function to_datestamp_stable(
time_int double precision
) returns date as $$
select date_trunc('day', to_timestamp($1))::date;
$$ language sql stable;
explain analyze
select to_datestamp_stable(a)
from generate_series(1, 1000000) s (a);
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------
Function Scan on generate_series s (cost=0.00..20.00 rows=1000 width=4) (actual time=91.924..3059.570 rows=1000000 loops=1)
Total runtime: 3103.655 ms
create or replace function to_datestamp_immutable(
time_int double precision
) returns date as $$
select date_trunc('day', to_timestamp($1))::date;
$$ language sql immutable;
explain analyze
select to_datestamp_immutable(a)
from generate_series(1, 1000000) s (a);
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------
Function Scan on generate_series s (cost=0.00..262.50 rows=1000 width=4) (actual time=92.639..20083.920 rows=1000000 loops=1)
Total runtime: 20149.311 ms