如何在不创建函数的情况下运行 plpgsql?

Posted

技术标签:

【中文标题】如何在不创建函数的情况下运行 plpgsql?【英文标题】:How to run plpgsql without creating a function? 【发布时间】:2016-07-13 15:18:39 【问题描述】:

我想在 Postgres 中以编程方式运行 SQL,而不创建函数。

原因:确保我的 plpgsql 事先工作并在将查询提交给函数之前“解释分析”查询。

我是 Postgres 的新手,我认为这很简单。我在那里找不到任何例子。也许这是不可能的?下面的代码如何工作?

DO
$body$
DECLARE
  v_name_short VARCHAR;
BEGIN

v_name_short := 'test Account 1';

     RETURN QUERY
        SELECT 
            a.name_short, 
            a.name_long
        FROM enterprise.account a 
        WHERE 
            CASE WHEN v_name_short IS NOT NULL THEN
               LOWER(a.name_short) = LOWER(v_name_short)
            ELSE
               1 = 1   
            END;
END;
$body$
LANGUAGE 'plpgsql';

同样,这里的目标是测试我的 SQL,就像在这种情况下,我想确保我的 CASE 语句仍在使用我创建的索引 (LOWER(name_short))。无论如何,我收到此错误消息:

错误:不能在非 SETOF 函数中使用 RETURN QUERY

我的要求在 Postgres 中可行吗?如果没有,有没有办法在函数内部查询分析计划?

【问题讨论】:

为什么要使用do 块来运行explain?为什么不能简单地运行explain select ... 【参考方案1】:

匿名代码块返回 void。但是,您可以使用临时表的技巧,例如

CREATE TEMP TABLE IF NOT EXISTS trace (name_short text, name_long text);

DO
$body$
DECLARE
    v_name_short VARCHAR;
BEGIN

    v_name_short := 'test Account 1';

    INSERT INTO trace 
        SELECT 
            a.name_short, 
            a.name_long
        FROM enterprise.account a 
        WHERE 
            CASE WHEN v_name_short IS NOT NULL THEN
               LOWER(a.name_short) = LOWER(v_name_short)
            ELSE
               1 = 1   
            END;
END;
$body$
LANGUAGE 'plpgsql'; 

SELECT * FROM trace;
-- DROP TABLE trace;

使用EXPLAIN ANALYSE,您只能分析单个普通 sql 查询,而不是函数、do 块或脚本。所以你可以试试:

EXPLAIN ANALYSE
    SELECT 
        a.name_short, 
        a.name_long
    FROM enterprise.account a 
    WHERE 
        CASE WHEN 'test Account 1' IS NOT NULL THEN
           LOWER(a.name_short) = LOWER('test Account 1')
        ELSE
           1 = 1   
        END;

请注意,在这种情况下,您不能使用变量,因为规划器无法识别它,请改用文字。

【讨论】:

克林,感谢您的回答。我只是很惊讶我不能在 Postgres 中干净地运行它。尽管您的解决方案执行了,但我无法从 pgAdmin 运行“解释分析”,因为它在 CREATE 语句中直接爆炸(无法分析)。我想我需要找到一种方法来分析函数内部的查询计划...... 您只能分析单个纯 sql 查询,不能分析函数、do 块或脚本。在这种情况下尝试explain analyse select ...,但您不能使用该变量。用文字 'test Account 1' 代替。 克林,帮个忙:您介意用您刚刚发布的评论更新您的答案吗?然后答案将完成,我可以将其标记为已接受。再次感谢您的帮助!【参考方案2】:

do匿名代码块总是返回void

代码块被视为没有参数的函数体,返回 void

要在 do 块内执行查询,请使用 perform

do $$
    begin
    perform * from t;
    end
$$;

https://www.postgresql.org/docs/current/static/plpgsql-statements.html#PLPGSQL-STATEMENTS-SQL-NORESULT

【讨论】:

谢谢,但是有没有办法用另一种语法来执行上面的代码? 我们更近了一步,但现在我明白了:“查询成功返回,28 毫秒内没有结果”。现在,有一个结果......但就像你说的“DO”总是返回无效。那么有没有办法在没有 DO 的情况下运行查询?再次感谢您的帮助!【参考方案3】:

在常规 PL/pgSQL 函数和 DO 语句之间还有第三个(未记录的)选项:临时函数

How to create a temporary function in PostgreSQL? Is there such thing as a "temp function"?

您实际上可以使用附加模块auto-explain 获得plpgsql 函数中每个SQL 语句的详细查询计划。详情:

Postgres query plan of a UDF invocation written in pgpsql

但是您必须使用 CASE 表达式中每个分支的值来测试您的函数,以确保涵盖所有内容。 Postgres 只对CREATE FUNCTION 进行表面语法检查。

Prepared statements 可能是另一个可以使用的选项。 PL/pgSQL 在内部处理 SQL 语句,就像准备好的语句一样:

Difference between language sql and language plpgsql in PostgreSQL functions

【讨论】:

以上是关于如何在不创建函数的情况下运行 plpgsql?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用列表的情况下转换字符串中的数字?

如何在不使用 @Composable 注释的情况下为撰写函数创建扩展?

在 C++ 中,如何在不使用 if 语句的情况下选择运行特定的成员函数?

Visual C++/Cli 中的异步睡眠,如何在不使 GUI 停止的情况下创建一个 X 毫秒来调用函数

如何在不删除旧文档的情况下将 XMLDocument 附加到 LocalStorage

如何在不使用 each() 的情况下重写此函数? [复制]