【问题标题】:PSQL replacement for stored procedure, too slow存储过程的 PSQL 替换,太慢了
【发布时间】:2017-04-24 13:28:53
【问题描述】:

我有property,每个propertycontracts,每个contract 有整数字段rental_area

以前我必须得到所有合同的rental_area,这很有效。

 SELECT 
        Sum(cr.rental_area)         total_property_rental_area,
        -- bunch of other cr fields
 FROM  appdata.contract_rental cr
        INNER JOIN appdata.domain_building b1 
                ON ( b1.building_id = cr.building_id ) 
        INNER JOIN appdata.domain_immovable im1 
                ON ( im1.immovable_id = b1.immovable_id ) 
 GROUP  BY im1.property_id

现在逻辑改变了,合约有一个periods 列表,其中一个周期包含该合约的rental_area。找到正确的period 需要一些特殊的逻辑。

我试图将逻辑加入到查询中,但找不到放置它的地方,所以我不得不创建存储过程。

SELECT Sum(p.rental_area) total_property_rental_area 
       -- bunch of other cr fields
FROM   appdata.contract_rental cr 
       JOIN appdata.rental_period p 
         ON p.id = Get_current_period_id(cr.contract_rental_id, 
                   cr.end_date_actual) 
       INNER JOIN appdata.domain_building b1 
               ON ( b1.building_id = cr.building_id ) 
       INNER JOIN appdata.domain_immovable im1 
               ON ( im1.immovable_id = b1.immovable_id ) 
GROUP  BY im1.property_id   

程序:

CREATE OR REPLACE FUNCTION appdata.get_current_period_id(in contract_id_in bigint, in end_date_actual_in Date)
  RETURNS bigint AS
$BODY$
DECLARE
    period_id bigint;
BEGIN
    -- find the period that matches with end date or current date
    select id into period_id
       from rental_period
       where contract_id = contract_id_in
         and Coalesce(end_date_actual_in, Now()) >= start_date
       order by start_date desc  limit 1;
    -- if there was no period, just take the first one
    IF period_id is null THEN
        select id into period_id
           from rental_period
           where contract_id = contract_id_in
           order by start_date asc
           limit 1;
    END IF;

    return period_id;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

但是现在速度太慢了,有没有办法不使用存储过程,把周期查找逻辑放到sql里面让它更快呢? 关键是,要达成合约,根据逻辑只能得到一个周期。

【问题讨论】:

  • 题外话:你可以避免 Coalesce(end_date_actual_in, Now()) 构造,方法是让 start_date 和 end_date 不可为空,并给它们默认值 -infinity+infinity
  • @joop ,但是我必须检查该值是 -infinity 还是 +infinity 而不是 null,所以我认为性能方面是相同的。同样在 UI 中,它们将始终被视为某些值,并会尝试在那里显示它们,而不是显示空字段。

标签: sql postgresql stored-procedures sqlperformance postgresql-performance


【解决方案1】:

将您的存储过程重新部署到主 SQL 中,主要提示是合并您的 2 个查询(如果一个为空,则使用另一个)

示例:代替

p.id = Get_current_period_id(cr.contract_rental_id, 
               cr.end_date_actual)

用途:

p.pid = coalesce(
    (select rpx.id 
     from rental_period rpx
     where contract_id = cr.contract_rental_id
     and Coalesce(cr.end_date, Now()) >= start_date
     order by start_date desc  limit 1;
    ),
  ( select rpy.id 
       from rental_period rpy
       where contract_id = cr.contract_rental_id
       order by start_date asc
       limit 1;
  )
)

根据下面的评论,以下索引也可能有所帮助:

 create index on rental_period (contract_id, start_date asc) 

请务必在之后分析表格以更新统计信息。

【讨论】:

  • 哇,它的工作原理,但我不明白如何?基本上是一样的查询,但是速度快很多?
  • 存储过程是所谓的优化“墙”。内部优化器尝试检查整个过程,以找出如何最好地获得结果。当放在一个 sql 语句中时,优化器有更多的信息并且更好地理解它在做什么。碰壁的那一刻,它就无法进一步优化……所以它可以独立地优化过程和 SQL 语句。将它们放在一起通常可以神奇地带来更好的性能。
  • 那些 order by's 和 limit 1's 也可能会被优化,如果由“where”字段中的所有列索引,它们也将执行得更好,并且具有正确的 asc/desc 顺序。如果您对结果感到满意,您可以随意停止尝试优化它。如果您希望它看起来更好一点,您可以使用“with”语句将查询划分为更易于消化的部分。 (查找 CTE 或公用表表达式)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-20
  • 2012-11-19
  • 2018-11-07
  • 2022-01-13
  • 1970-01-01
  • 2017-01-26
  • 1970-01-01
相关资源
最近更新 更多