将 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 函数分解成更小的部分的主要内容,如果未能解决你的问题,请参考以下文章