postgresql - Postgres 查询不会在函数中完成,但如果单独运行它可以工作

标签 postgresql function sql-execution-plan

这是我的函数的简化版本,其中包含查询(因此任何变量现在都无用),并且该函数不会完成,但如果我单独运行相同的查询,它会在一秒钟内完成。

永远不会完成的函数

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 之前提高 default_statistics_target

如果您需要提高 default_statistics_target 以获得更好的估计,请保留该更改

ALTER TABLE ui_cdc_s5_misto_cas_zdroj_aggregace
   ALTER datum SET STATISTICS <whatever proved useful>

关于postgresql - Postgres 查询不会在函数中完成,但如果单独运行它可以工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54945240/

相关文章:

postgresql - 我可以使用什么 select 语句来确定 1) 用户所属的角色和 2) 用户使用 PostgreSQL 可以访问的表?

node.js - 使用 Sequelize.js 和 PostgreSQL 查询关联模型上的 JSONB 字段

c# - 循环中的两个 0 到 1 方向

sql - 如何在 SQL Server 中获取查询执行计划?

mysql - 为什么不使用和使用 EXPLAIN 的查询显示不同的行数?

SQL 操作顺序

sql - 在 PostgreSQL 中更新具有非空和唯一约束的列的特殊情况

postgresql - 使用哈希值设置 postgres 密码

javascript - 为什么我的代码会执行在 return 语句之后定义的函数?

function - 在 ELF 中查找函数的起始偏移量