Postgres 查询不会在函数中完成,但如果 运行 分开它会工作
Postgres query won't finish in function but if run separately it works
这是我的函数的简化版本,其中包含查询(因此任何变量现在都无用)并且此函数不会完成,但如果我 运行 单独执行相同的查询,它会在一秒钟内完成.
永远不会完成的函数
select * from test_function_difference(1);
CREATE OR REPLACE FUNCTION test_function_difference (
p_does_nothing int
)
RETURNS TABLE(
t_datum date,
t_capacity numeric,
t_used numeric,
t_category int,
t_category_name text,
t_used_p numeric,
t_unused_p numeric
)
VOLATILE
AS $dbvis$
declare
p_sql text := '';
p_execute text := '';
rec record;
begin
p_sql :=
'
with
vytizeni as (
select
date_trunc(''day'',mcz.datum)::date as datum ,
sum(zd.v_vytizeni)/3600.0 used
from v_ui_cdc_s5_misto_cas_zdroj_aggregace mcz
left join (select * , pul_den as den_noc from v_ui_cdc_s5_misto_cas_zdroj_aggregace_zdrobneni) zd on mcz.id = zd.id
where
datum between ''2018-12-31'' and ''2018-12-31''
and ( zahranicni = 0 or zahranicni is null )
and den_noc = -1
group by
date_trunc(''day'',mcz.datum)::date
)
,kapacita as (
select
date_trunc(''day'',datum)::date as datum ,
sum(obsazeni_g)/3600.0 capacity
from v_ui_cdc_s5_misto_cas_zdroj_aggregace
where
datum between ''2018-12-31'' and ''2018-12-31''
group by
date_trunc(''day'',datum)::date
)
,zdroj as (
select
k.datum,
k.capacity,
v.used,
-1 category
from kapacita k
join vytizeni v on k.datum = v.datum
)
select
c.* ,
kc.nazev::text categeroy_name,
case when sum(capacity)over(partition by datum) = 0 then 1 else used/sum(capacity)over(partition by datum) end as used_p,
greatest(1 - case when sum(capacity)over(partition by datum) = 0 then 1 else sum(used)over(partition by datum)/sum(capacity)over(partition by datum) end,0) as unused_p
from zdroj c
left join v_ui_cdc_s5_kategorie_cinnosti kc on kc.id = c.category
order by c.datum
';
raise notice '% ' , p_sql;
RETURN QUERY
execute p_sql;
END;
$dbvis$ LANGUAGE plpgsql
和查询我 运行 分开(在 533 毫秒内完成)
with
vytizeni as (
select
date_trunc('day',mcz.datum)::date as datum ,
sum(zd.v_vytizeni)/3600.0 used
from v_ui_cdc_s5_misto_cas_zdroj_aggregace mcz
left join (select * , pul_den as den_noc from v_ui_cdc_s5_misto_cas_zdroj_aggregace_zdrobneni) zd on mcz.id = zd.id
where
datum between '2018-12-31' and '2018-12-31'
and ( zahranicni = 0 or zahranicni is null )
and den_noc = -1
group by
date_trunc('day',mcz.datum)::date
)
,kapacita as (
select
date_trunc('day',datum)::date as datum ,
sum(obsazeni_g)/3600.0 capacity
from v_ui_cdc_s5_misto_cas_zdroj_aggregace
where
datum between '2018-12-31' and '2018-12-31'
group by
date_trunc('day',datum)::date
)
,zdroj as (
select
k.datum,
k.capacity,
v.used,
-1 category
from kapacita k
join vytizeni v on k.datum = v.datum
)
select
c.* ,
kc.nazev::text categeroy_name,
case when sum(capacity)over(partition by datum) = 0 then 1 else used/sum(capacity)over(partition by datum) end as used_p,
greatest(1 - case when sum(capacity)over(partition by datum) = 0 then 1 else sum(used)over(partition by datum)/sum(capacity)over(partition by datum) end,0) as unused_p
from zdroj c
left join v_ui_cdc_s5_kategorie_cinnosti kc on kc.id = c.category
order by c.datum
编辑:我在将近 28 分钟后能够从函数中获得结果(我也在周日晚上尝试过,这意味着我拥有整个服务器的资源,因为在正常加载期间函数甚至在一个之后还没有完成小时)然后我 运行 独立查询并在 2.1 秒后得到结果 这是解释分析
功能:28分钟
https://explain.depesz.com/s/v9xJ
独立查询:2.1 秒
https://explain.depesz.com/s/aBri
秒运行单机430ms
https://explain.depesz.com/s/ENva
有趣的注意事项:如果我将间隔的开始日期编辑为“2018-12-30”或任何其他日期,函数也会完成
这意味着
start date = '2018-12-31'
query => finishes under 1 second
function => won't finish
start date = '2018-12-30'
query => finishes under 1 second
function => finishes under 1 second
版本详细信息:x86_64-pc-linux-gnu 上的 PostgreSQL 10.7,由 gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36) 编译,64 位
造成性能差异的原因是函数内部的执行没有使用并行查询,并行执行随机选择了一个更好的执行计划。
该函数是否未标记为并行安全?这可能会有所不同。
但是,核心问题是 ui_cdc_s5_misto_cas_zdroj_aggregace
上扫描的结果行数的粗密斯估计,估计是 1 行而不是 2243 行。
您应该 ANALYZE
那 table 以获得更好的估计。如果仅此一项不能改善估计,请尝试在 ANALYZE
.
之前提高 default_statistics_target
如果您需要提高 default_statistics_target
以获得更好的估计,请通过
坚持更改
ALTER TABLE ui_cdc_s5_misto_cas_zdroj_aggregace
ALTER datum SET STATISTICS <whatever proved useful>
这是我的函数的简化版本,其中包含查询(因此任何变量现在都无用)并且此函数不会完成,但如果我 运行 单独执行相同的查询,它会在一秒钟内完成.
永远不会完成的函数
select * from test_function_difference(1);
CREATE OR REPLACE FUNCTION test_function_difference (
p_does_nothing int
)
RETURNS TABLE(
t_datum date,
t_capacity numeric,
t_used numeric,
t_category int,
t_category_name text,
t_used_p numeric,
t_unused_p numeric
)
VOLATILE
AS $dbvis$
declare
p_sql text := '';
p_execute text := '';
rec record;
begin
p_sql :=
'
with
vytizeni as (
select
date_trunc(''day'',mcz.datum)::date as datum ,
sum(zd.v_vytizeni)/3600.0 used
from v_ui_cdc_s5_misto_cas_zdroj_aggregace mcz
left join (select * , pul_den as den_noc from v_ui_cdc_s5_misto_cas_zdroj_aggregace_zdrobneni) zd on mcz.id = zd.id
where
datum between ''2018-12-31'' and ''2018-12-31''
and ( zahranicni = 0 or zahranicni is null )
and den_noc = -1
group by
date_trunc(''day'',mcz.datum)::date
)
,kapacita as (
select
date_trunc(''day'',datum)::date as datum ,
sum(obsazeni_g)/3600.0 capacity
from v_ui_cdc_s5_misto_cas_zdroj_aggregace
where
datum between ''2018-12-31'' and ''2018-12-31''
group by
date_trunc(''day'',datum)::date
)
,zdroj as (
select
k.datum,
k.capacity,
v.used,
-1 category
from kapacita k
join vytizeni v on k.datum = v.datum
)
select
c.* ,
kc.nazev::text categeroy_name,
case when sum(capacity)over(partition by datum) = 0 then 1 else used/sum(capacity)over(partition by datum) end as used_p,
greatest(1 - case when sum(capacity)over(partition by datum) = 0 then 1 else sum(used)over(partition by datum)/sum(capacity)over(partition by datum) end,0) as unused_p
from zdroj c
left join v_ui_cdc_s5_kategorie_cinnosti kc on kc.id = c.category
order by c.datum
';
raise notice '% ' , p_sql;
RETURN QUERY
execute p_sql;
END;
$dbvis$ LANGUAGE plpgsql
和查询我 运行 分开(在 533 毫秒内完成)
with
vytizeni as (
select
date_trunc('day',mcz.datum)::date as datum ,
sum(zd.v_vytizeni)/3600.0 used
from v_ui_cdc_s5_misto_cas_zdroj_aggregace mcz
left join (select * , pul_den as den_noc from v_ui_cdc_s5_misto_cas_zdroj_aggregace_zdrobneni) zd on mcz.id = zd.id
where
datum between '2018-12-31' and '2018-12-31'
and ( zahranicni = 0 or zahranicni is null )
and den_noc = -1
group by
date_trunc('day',mcz.datum)::date
)
,kapacita as (
select
date_trunc('day',datum)::date as datum ,
sum(obsazeni_g)/3600.0 capacity
from v_ui_cdc_s5_misto_cas_zdroj_aggregace
where
datum between '2018-12-31' and '2018-12-31'
group by
date_trunc('day',datum)::date
)
,zdroj as (
select
k.datum,
k.capacity,
v.used,
-1 category
from kapacita k
join vytizeni v on k.datum = v.datum
)
select
c.* ,
kc.nazev::text categeroy_name,
case when sum(capacity)over(partition by datum) = 0 then 1 else used/sum(capacity)over(partition by datum) end as used_p,
greatest(1 - case when sum(capacity)over(partition by datum) = 0 then 1 else sum(used)over(partition by datum)/sum(capacity)over(partition by datum) end,0) as unused_p
from zdroj c
left join v_ui_cdc_s5_kategorie_cinnosti kc on kc.id = c.category
order by c.datum
编辑:我在将近 28 分钟后能够从函数中获得结果(我也在周日晚上尝试过,这意味着我拥有整个服务器的资源,因为在正常加载期间函数甚至在一个之后还没有完成小时)然后我 运行 独立查询并在 2.1 秒后得到结果 这是解释分析
功能:28分钟 https://explain.depesz.com/s/v9xJ
独立查询:2.1 秒 https://explain.depesz.com/s/aBri
秒运行单机430ms https://explain.depesz.com/s/ENva
有趣的注意事项:如果我将间隔的开始日期编辑为“2018-12-30”或任何其他日期,函数也会完成
这意味着
start date = '2018-12-31'
query => finishes under 1 second
function => won't finish
start date = '2018-12-30'
query => finishes under 1 second
function => finishes under 1 second
版本详细信息:x86_64-pc-linux-gnu 上的 PostgreSQL 10.7,由 gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36) 编译,64 位
造成性能差异的原因是函数内部的执行没有使用并行查询,并行执行随机选择了一个更好的执行计划。
该函数是否未标记为并行安全?这可能会有所不同。
但是,核心问题是 ui_cdc_s5_misto_cas_zdroj_aggregace
上扫描的结果行数的粗密斯估计,估计是 1 行而不是 2243 行。
您应该 ANALYZE
那 table 以获得更好的估计。如果仅此一项不能改善估计,请尝试在 ANALYZE
.
default_statistics_target
如果您需要提高 default_statistics_target
以获得更好的估计,请通过
ALTER TABLE ui_cdc_s5_misto_cas_zdroj_aggregace
ALTER datum SET STATISTICS <whatever proved useful>