编写/调试复杂 PL/pgSQL 查询的最佳实践

Posted

技术标签:

【中文标题】编写/调试复杂 PL/pgSQL 查询的最佳实践【英文标题】:Best practices for writing / debugging complex PL/pgSQL queries 【发布时间】:2014-11-23 23:37:19 【问题描述】:

简而言之:使用过程编程技术(例如使用 pl/pgsql)编写和调试 PostgreSQL 查询的最佳方法是什么?我对编写这些类型的程序查询很陌生,所以如果我还没有找到一个非常简单的解决方案,请提前道歉。

细节:我正在编写一些(相对)复杂的 SQL 查询,它们利用现有的 pl/pgsql 函数,理想情况下会利用大量变量来使查询易于调试和修改。 (下面的示例代码故意简单;我的实际用例要复杂得多。)

我的第一个解决方案是将所有内容嵌入到函数中,但这很笨重。由于我必须显式声明返回类型,因此修改代码很痛苦:

CREATE OR REPLACE FUNCTION 
my_schema.my_fcn()
RETURNS TABLE(user_id integer, 
              id integer, 
              created_at timestamp) AS
$BODY$
DECLARE
    _id_select integer = 10;
BEGIN
    RETURN QUERY
        SELECT 
            user_id, 
            id,
            created_at
        WHERE id = _id_selector
        -- possibly a ton of other complicated stuff involving other functions and variables
        FROM my_schema.my_other_fcn()

END;
$BODY$
LANGUAGE plpgsql;

(编辑:使查询更加不平凡。)

现在假设我想在查询中添加order_total 并去掉id:那么我必须修改返回类型。这会经常发生,而且必须一遍又一遍地更改返回类型,这很烦人。

我尝试的下一个解决方案是不使用函数,并尝试以下方式:How can I execute pl/pgsql code without creating a function? 这也不起作用:

DO $$ 
DECLARE
    _id_select integer = 10;
BEGIN
  SELECT 
      user_id, 
      id,
      created_at
  WHERE id = _id_selector
  -- possibly a ton of other complicated stuff involving other functions and variables
  FROM my_schema.my_other_fcn()
END $$;

产生的错误:

ERROR:  query has no destination for result data
HINT:  If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT:  PL/pgSQL function inline_code_block line 4 at SQL statement
********** Error **********

ERROR: query has no destination for result data

我不想使用PERFORM,因为实际上我确实想查看查询结果。

这种类型的 SQL 编程是否有优雅和/或标准的解决方案?

【问题讨论】:

【参考方案1】:

为什么不直接调用plpgsql函数?

只做:

选择 用户身份, ID, created_at -- 可能还有很多涉及其他函数和变量的复杂内容 FROM my_schema.my_other_fcn()

我不喜欢这种风格的 plpgsql 编程——包装查询。您可以阻止非常有效的规划器 - 因为 plpgsql 函数是优化器的黑匣子 - 所以您可以预料到大数据上会出现可怕的性能问题。 PLpgSQL 函数不应替代视图。

语句 DO 不应返回任何结果。简单地说——PostgreSQL 函数不应该替代 PostgreSQL 视图。

【讨论】:

像这样直接调用嵌入式函数作为查询(据我所知)不允许您在查询中使用变量、循环等。这就是-- possibly a ton of other complicated stuff involving other functions and variables 的意图。关于性能,在这种情况下,这不是我关心的问题。 如果你需要一个变量、循环和可能的返回值,那么你需要使用函数——DO语句太有限了——没有任何其他可能。

以上是关于编写/调试复杂 PL/pgSQL 查询的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

PL/pgSQL 函数中的可选参数

在 PL/pgSQL 函数中使用变量

为啥我不能从 pgAdmin pgScript 调用 PL/pgSQL 函数?

如何从 C++ 代码调用 PL/pgSQL 函数

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

PL/pgSQL 函数在 pgAdmin 之外无法正确运行