在 PL/pgSQL 块中运行 SELECT

Posted

技术标签:

【中文标题】在 PL/pgSQL 块中运行 SELECT【英文标题】:Running a SELECT in a PL/pgSQL block 【发布时间】:2015-07-07 06:10:00 【问题描述】:

我是 PostgreSQL 的新手,我必须在 s-s-rS 报告中使用内联查询来从 PostgreSQL 数据库中获取数据。

场景是:根据报告参数的选定值,我需要从不同的表中获取输出。请参阅下面的示例内联查询。

    DO
    $do$

    BEGIN
    IF ($1 = 'Monthly') THEN

    SELECT *
    FROM table1;

    ELSE 

    SELECT *
    FROM table2;

    END IF;

    END
    $do$

以上查询报错,

错误:查询没有结果数据的目的地 SQL 状态:42601 提示:如果要丢弃 SELECT 的结果,请改用 PERFORM。 上下文:SQL 语句中的 PL/pgSQL 函数 inline_code_block 第 6 行

请注意,我不能使用存储过程或函数来检索所需的数据,我只能使用内联查询。

有人可以告诉我如何解决上述错误吗?

【问题讨论】:

选择的结果需要去某处 - 所以你需要把结果放到一个变量中。而且您的代码减少了很多,以至于不再有意义:if (1 > 0) 将始终为真,第二条语句将永远不会被执行。您可以将整个块替换为select * from table1 请在下面找到更新后的查询,DO $do$ BEGIN IF ($1 = 'Monthly') THEN SELECT * FROM table1; ELSE SELECT * FROM table2;万一;结束$do$ 您不能将参数传递给匿名代码块,也不能从中返回结果。所以你的提议永远不会奏效。 感谢帕特里克的回复。那么是否有任何解决方法来实现所需的行为?这本来是 SQL Server 中的一个简单查询,但我发现 Postgresql 不支持类似的东西令人失望。 嗯,每个 DBMS 都是不同的。您总是会在一个 DBMS 中找到无法直接移植到另一个 DBMS 的东西。我可以说出许多 SQL Server 无法做到但在 Postgres 中非常简单的事情。此外:您也无法在 Oracle 或 DB2 中做类似的事情。 【参考方案1】:

您的示例有两个问题 - DO 语句(匿名块)不支持

    参数 返回结果。

PostgreSQL 不支持在 T-SQL 或 MS-SQL 中使用的称为未绑定查询的技术。每个查询都必须有指定的目标。您可以改用函数(table1table2 应该具有相同的结构):

CREATE OR REPLACE FUNCTION foo(frequency)
RETURNS SETOF table1 AS $$
BEGIN
  IF $1 = 'Monthly' THEN
    RETURN QUERY SELECT * FROM table1;
  ELSE
    RETURN QUERY SELECT * FROM table2;
  END IF;
  RETURN;
END;
$$ LANGUAGE plpgsql;

SELECT * FROM foo('Monthly');

【讨论】:

您好帕维尔,感谢您的意见。上述解决方案在我的场景中不起作用,因为我们无法在 Postgresql DB 中创建任何函数/存储过程。我们只能使用内联查询来获取数据。请让我知道是否有任何其他方法可以实现相同的目标? @VinayakZirmirkar - 那么你必须在应用程序中做出这个决定,或者像 Gary 描述的那样使用 UNION。没有其他办法。【参考方案2】:

假设表具有相同的列结构,您可以使用联合在单个查询中执行这两个选项。

SELECT * FROM table1 WHERE $1 = 'Monthly'
UNION ALL
SELECT * FROM table2 WHERE NOT ($1 = 'Monthly')

【讨论】:

【参考方案3】:

您可以创建一个临时表来获取外部结果,但不能传递参数:

DO
$$
BEGIN
IF <> THEN
CREATE TEMPORARY TABLE foo AS
SELECT *FROM ...
 ELSE

 ....
 END IF;
 END
 $$
 SELECT * FROM FOO;

【讨论】:

【参考方案4】:

如果你创建一个接受'select'查询文本并返回SETOF RECORD的函数,那么你可以直接在sql窗口中执行:

with c_sql as (
-- >>> --- your any query
select STRING_AGG('select ''"' || t.table_schema || '"."' || t.table_name || '"'' tab, count(*) cnt from "' || t.table_schema || '"."' || t.table_name || '"', ' union all ')
|| ' order by 2 desc' v_sql
from information_schema.tables t
where t.table_schema like 'tiger'
-- <<< ---
)
select ex.* from c_sql
LEFT JOIN lateral ( select * from execsql(c_sql.v_sql) as ss(tab text, cnt int8) ) ex on true

这是execsql函数:

CREATE OR REPLACE FUNCTION public.execsql(
text)
RETURNS SETOF RECORD
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN 
    RETURN QUERY EXECUTE $1 ; 
END 
$BODY$;

请注意,您应该将 execsql 函数的结果转换为 与您的“选择”查询匹配的特定记录结构。


PS: 顺便说一句,我不明白为什么没有人推荐这种执行几乎所有请求的简单方法。这是一种非常有用的方法,可以快速形成查询并执行它,而无需创建阻塞数据库的单独函数。

【讨论】:

以上是关于在 PL/pgSQL 块中运行 SELECT的主要内容,如果未能解决你的问题,请参考以下文章

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

在 PL/pgSQL 函数中使用变量

有没有为 PostgreSQL 开发的 PL/pgSQL 免费环境?

PL/pgSQL 语法错误

PostgreSQL vs Oracle:PL/pgSQL 的“编译时”检查

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