为什么当我第二次使用完全相同的参数调用 IMMUTABLE 函数时,计划时间会加倍?
Why plainning time is doubled when I call IMMUTABLE function with exatly same arguments second time?
比较:
在第一种情况下,我用相同的参数调用相同的 find_period
。因为函数是 IMMUTABLE
我想它的计划将被重用(相同的函数,相同的参数,相同的计划),但似乎没有被重用 为什么?
源函数:
CREATE OR REPLACE FUNCTION "find_period" (in _start timestamptz, in _interval interval, in _target timestamptz)
RETURNS tstzrange
LANGUAGE sql
IMMUTABLE RETURNS NULL ON NULL INPUT
AS $$
SELECT CASE
WHEN _interval = INTERVAL '00:00:00' THEN
tstzrange( _start, _start, '[]' )
ELSE (
SELECT CASE WHEN max( date ) = _target
THEN tstzrange( max( date ) -_interval, max( date ) )
ELSE tstzrange( max( date ), max( date ) +_interval )
END
FROM generate_series( _start, _target, _interval ) t (date )
) END
WHERE _start < _target OR _interval = INTERVAL '00:00:00'
$$
并查询:
EXPLAIN ANALYZE SELECT find_period(
'2020-04-03',
INTERVAL '1day',
'9999-01-01'
)
UPD
EXPLAIN ( ANALYZE, buffers, timing )
第一次调用的缓冲区是 12808
VS 6404
第二个:
db=> select version();
version
------------------------------------------------------------------------------------------
PostgreSQL 13.1 (Debian 13.1-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian
(1 row)
db=> EXPLAIN ( ANALYZE, buffers, timing ) SELECT find_period(
'2020-04-03',
INTERVAL '1day',
'9999-01-01'
), find_period(
db(> '2020-04-03',
db(> INTERVAL '1day',
db(> '9999-01-01'
db(> );
QUERY PLAN
-------------------------------------------------------------------------------------
Result (cost=0.00..0.01 rows=1 width=64) (actual time=0.003..0.003 rows=1 loops=1)
Planning:
Buffers: temp read=12808 written=12808
Planning Time: 3215.465 ms
Execution Time: 0.023 ms
(5 rows)
db=> EXPLAIN ( ANALYZE, buffers, timing ) SELECT find_period(
db(> '2020-04-03',
db(> INTERVAL '1day',
db(> '9999-01-01'
db(> );
QUERY PLAN
-------------------------------------------------------------------------------------
Result (cost=0.00..0.01 rows=1 width=32) (actual time=0.002..0.002 rows=1 loops=1)
Planning:
Buffers: shared hit=97, temp read=6404 written=6404
Planning Time: 1583.670 ms
Execution Time: 0.017 ms
(5 rows)
与您预期的不同,PostgreSQL 调用该函数两次,因为它不会花费额外的计划工作来检查您是否使用相同的常量调用相同的函数两次。
做合理的事情:
SELECT x, x
FROM find_period('2020-04-03', INTERVAL '1day', '9999-01-01' ) AS f(x);
比较:
在第一种情况下,我用相同的参数调用相同的 find_period
。因为函数是 IMMUTABLE
我想它的计划将被重用(相同的函数,相同的参数,相同的计划),但似乎没有被重用 为什么?
源函数:
CREATE OR REPLACE FUNCTION "find_period" (in _start timestamptz, in _interval interval, in _target timestamptz)
RETURNS tstzrange
LANGUAGE sql
IMMUTABLE RETURNS NULL ON NULL INPUT
AS $$
SELECT CASE
WHEN _interval = INTERVAL '00:00:00' THEN
tstzrange( _start, _start, '[]' )
ELSE (
SELECT CASE WHEN max( date ) = _target
THEN tstzrange( max( date ) -_interval, max( date ) )
ELSE tstzrange( max( date ), max( date ) +_interval )
END
FROM generate_series( _start, _target, _interval ) t (date )
) END
WHERE _start < _target OR _interval = INTERVAL '00:00:00'
$$
并查询:
EXPLAIN ANALYZE SELECT find_period(
'2020-04-03',
INTERVAL '1day',
'9999-01-01'
)
UPD
EXPLAIN ( ANALYZE, buffers, timing )
第一次调用的缓冲区是 12808
VS 6404
第二个:
db=> select version();
version
------------------------------------------------------------------------------------------
PostgreSQL 13.1 (Debian 13.1-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian
(1 row)
db=> EXPLAIN ( ANALYZE, buffers, timing ) SELECT find_period(
'2020-04-03',
INTERVAL '1day',
'9999-01-01'
), find_period(
db(> '2020-04-03',
db(> INTERVAL '1day',
db(> '9999-01-01'
db(> );
QUERY PLAN
-------------------------------------------------------------------------------------
Result (cost=0.00..0.01 rows=1 width=64) (actual time=0.003..0.003 rows=1 loops=1)
Planning:
Buffers: temp read=12808 written=12808
Planning Time: 3215.465 ms
Execution Time: 0.023 ms
(5 rows)
db=> EXPLAIN ( ANALYZE, buffers, timing ) SELECT find_period(
db(> '2020-04-03',
db(> INTERVAL '1day',
db(> '9999-01-01'
db(> );
QUERY PLAN
-------------------------------------------------------------------------------------
Result (cost=0.00..0.01 rows=1 width=32) (actual time=0.002..0.002 rows=1 loops=1)
Planning:
Buffers: shared hit=97, temp read=6404 written=6404
Planning Time: 1583.670 ms
Execution Time: 0.017 ms
(5 rows)
与您预期的不同,PostgreSQL 调用该函数两次,因为它不会花费额外的计划工作来检查您是否使用相同的常量调用相同的函数两次。
做合理的事情:
SELECT x, x
FROM find_period('2020-04-03', INTERVAL '1day', '9999-01-01' ) AS f(x);