将 PL/pgSQL 函数分解成更小的部分

Posted

技术标签:

【中文标题】将 PL/pgSQL 函数分解成更小的部分【英文标题】:Break PL/pgSQL functions into smaller parts 【发布时间】:2014-10-21 11:53:56 【问题描述】:

我想编写一个模块化的 API 函数。问题是,模块化部件功能 相同的查询。我怀疑,每个子功能中的相同查询都会影响性能。

例子:

    MAIN_FUNCTION: base_data(customer_id, user_id) 
      SELECT *
        FROM get_absence(customer_id, user_id)
        JOIN get_work_hours(customer_id, user_id)
       USING (worker_id)
    

    get_absence(customer_id, user_id) 
      RETURN QUERY
      SELECT *
        FROM get_user_workers(customer_id, user_id)
        JOIN absence_table
       USING (worker_id)
    

    get_work_hours(customer_id, user_id) 
      RETURN QUERY
      SELECT *
        FROM get_user_workers(customer_id, user_id)
        JOIN workhours_table
       USING (worker_id)
    

抱歉伪代码,但它有点短。基本上,大多数子函数将包含 get_user_workers 查询。如果我要构建一个大而乏味的函数,我会使用公共表表达式查询工作人员。

如何实现可读性和模块化,而不是无数行查询?

【问题讨论】:

总是包含一个完整的函数头(和页脚),包括名称、参数、数据类型、返回类型、语言声明……以及当然,你的 Postgres 版本。 你的最后一句话真的是你的问题吗?您的帖子的其余部分听起来像是您真的在问,“我如何避免多次调用 get_user_workers() ”? 从性能角度来看,这种风格是最差的。您可以非常有效地阻止查询优化器。 SQL 不支持这种小粒度。如果需要,请使用视图而不是简单的一行 sql 查询函数。 我认为添加完整的页眉和页脚没有意义,因为问题不在于小的调整,而在于一般的方法。不,问题不仅在于如何避免调用 functionX N 次,还在于如何获得优雅且可维护的代码。 【参考方案1】:

可以轻松做到这一点:

CREATE FUNCTION get_absence(...)  RETURNS TABLE (...) ...;
CREATE FUNCTION get_work_hours(...) RETURNS TABLE (...) ...;

CREATE FUNCTION base_data(_customer_id int, _user_id int)
  RETURNS TABLE (...) AS
$func$
   SELECT a.*, w.*
   FROM   get_absence(_customer_id, _user_id) a
   JOIN   get_work_hours(_customer_id, _user_id) w ON w.??? = a.???;
$func$ LANGUAGE sql;

使用 SQL 函数作为简单示例。这同样适用于 PL/pgSQL,因为问题实际上是 SQL 问题。

FROM 子句中,可以像使用表格一样使用集合返回函数。但是 调用 set-returning 函数有:

<b>SELECT * FROM</b> base_data(1,2);

请注意,简单的 SQL 函数可以“内联”,而 PL/pgSQL 函数总是单独执行(构成优化障碍)——但它们的执行计划被保存并在同一个会话中重复使用(如果 Postgres 发现这是便宜)。各有优缺点。

通常,没有子功能的单个、集成和优化的查询是最快的。根据许多情况,可能几乎不重要或产生相关影响。

详情:

Difference between language sql and language plpgsql in PostgreSQL functions PostgreSQL Stored Procedure Performance

【讨论】:

我认为这是错误的策略——这应该用视图来完成,而不是函数。你试图合并功能世界和关系世界。它应该适用于较小的项目,其中查询优化并不重要,但对于大型数据库,这种策略是完全错误的 - 功能性 API 必须包装完整的优化查询。 好的,这个函数看起来不是很干净。然后 base_data 应该包含所有将要使用的函数,我不能在较低级别隐藏一些函数。谢谢帕维尔,我会尝试使用视图的道。 唯一困扰我的观点是我不能在其中使用 generate_series。这意味着 main 函数中的大量 SQL 被视图包围。 ***.com/a/25256211/1190411 似乎我可以在视图中使用会话变量。谢谢大家。 @user1190411:这是个误会。当然你可以在函数中使用generate_series()。此外,一个简单的IMMUTABLE sql 函数(例如SELECT 1234)就可以提供等效的全局变量。您可能会发布一个带有(简化的)全貌的新问题,以获得最佳解决方案。

以上是关于将 PL/pgSQL 函数分解成更小的部分的主要内容,如果未能解决你的问题,请参考以下文章

分解成更小的查询

如何将文本文件分解成更小的块(在 Unix 上使用 C++)?

有没有办法拆分/分解 Gradle 构建的常见部分

PHP面向对象所学部分

面向对象相关概述

如果我想将视图分解为更小的子视图,我应该在 ASP.NET MVC 中创建啥项目?