PL/pgSQL 代码的问题

Posted

技术标签:

【中文标题】PL/pgSQL 代码的问题【英文标题】:Issues with PL/pgSQL code 【发布时间】:2014-07-08 21:17:38 【问题描述】:

我在 plpgsql 方面不是很有经验,所以我在这里有 2 个问题。

    在 Postgres 中是否可以像在动态 sql's for plsql 中那样使用游标?基本上我不想在 plsql 中使用过程,所以我创建了一个游标,它将保存我的动态查询的输出,然后我在另一个查询中的循环中使用该值以使我的输出显示在屏幕上。我正在尝试使用 Postgres 做同样的事情,但无法做到。 在这种情况下是否可以避免创建永久函数来完成此任务?

这是我运行良好的 oracle 脚本:

DECLARE
  CURSOR cur_tables IS
     SELECT  NAME,
'SELECT PROPERTY_VALUE FROM '
         || USERNAME
         || '.P_PROPERTY WHERE PROPERTY_NAME = ''VERSION'''
                 AS dsql
       FROM CB_DATASOURCE
      WHERE (UPPER(USERNAME) LIKE 'NAV_PS_%' or UPPER(USERNAME) LIKE 'CBPS_%' or UPPER(USERNAME) LIKE 'DEFAULTPS');
  CURR_VERSION VARCHAR2(1000);
BEGIN
  FOR r_tables IN cur_tables LOOP
   begin
     EXECUTE IMMEDIATE r_tables.dsql INTO CURR_VERSION;
     DBMS_OUTPUT.put_line(r_tables.NAME || ': ' || CURR_VERSION);
    exception
     when others then
      DBMS_OUTPUT.put_line(r_tables.NAME || ' no table');
   end;   
  END LOOP;
END;
/

这是我的 postgres 函数,我无法让它工作,但最终想避免使用永久函数

create or replace function upgrade_version() returns setof record as $$ 
declare
 r record;
 loopy record;
 isql text;
 CURR_VERSION text;
begin
 for r in SELECT 'SELECT PROPERTY_VALUE FROM '
         || USERNAME
         || '.P_PROPERTY WHERE PROPERTY_NAME = ''VERSION'''
                 AS dsql
       FROM CB_DATASOURCE
      WHERE (UPPER(USERNAME) LIKE 'NAV_PS_%' or UPPER(USERNAME) LIKE 'CBPS_%' or UPPER(USERNAME) LIKE 'DEFAULTPS') loop
  isql := r.dsql;
  EXECUTE isql INTO CURR_VERSION;
  RETURN next loopy;
 end loop;
 return;
end;
$$ language 'plpgsql';

我非常感谢您对此的任何意见。

【问题讨论】:

程序语言的名称是PL/pgSQL or simply plpgsql。 RDBMS 的名称是PostgreSQL or Postgres for short。至少你可以自己发现。请提出一个中规中矩的问题,不要害怕formatting aids。 感谢您的意见。下次我会尝试提出一个体面的问题。 旁白:不要引用LANGUAGE plpgsql 中的语言名称。它是一个标识符,而不是一个字符串。目前可以容忍,但可能会在未来的版本中消失。 【参考方案1】:

您似乎正在尝试的应该与DO statement 一起使用:

DO
$do$ 
DECLARE
   r record;
   curr_version text;
BEGIN
   FOR r IN 
      SELECT name, format($$
         SELECT property_value
         FROM   %I.p_property
         WHERE  property_name = 'VERSION'$$, username) AS dsql
      FROM   cb_datasource
      WHERE  upper(username) LIKE ANY ('NAV_PS_%, CBPS_%, DEFAULTPS')
   LOOP
      BEGIN
         EXECUTE r.dsql INTO curr_version;
         RAISE NOTICE '%: %', r.name, curr_version;
      EXCEPTION WHEN OTHERS THEN
         RAISE NOTICE '%: no table', r.name;
      END;   
   END LOOP;
END
$do$

要点

您不能在 DO 语句中使用 RETURN,但您可以使用 RAISE 通知等。 似乎很适合你,因为你eventually would like to refrain from using a permanent function。 请注意,DO 语句的默认过程语言仍然是 plpgsql

在 Postgres 中,未加引号的标识符被转换为小写(与 Oracle 不同,它们是大写的)。

要在循环中捕获异常,您需要将主体包装在单独的块中。 Read the manual here.

您需要清理动态构建的 SQL 字符串中的标识符,以免您容易受到 SQL 注入和非标准名称的其他问题的影响。那里也缺少您的 Oracle 代码。 我为此目的使用format()%I。更多信息:How to use EXECUTE FORMAT ... USING in postgres function

WHERE 子句缩短为ANY

您知道_LIKE 模式中的占位符吗?对于文字 _,将其转义:\_

【讨论】:

【参考方案2】:

您可以使用 DO $$ $$ 脚本避免创建永久函数:http://www.postgresql.org/docs/9.3/static/sql-do.html

但是,鉴于您正在根据另一个查询的内容定义游标,我不清楚您将如何完全避免使用 PL/pgSQL 代码。您在 Oracle 中使用 PL/SQL 代码;为什么在 PostgreSQL 中不需要过程代码?

如果您喜欢游标,当然可以在您的 PL/pgSQL 函数中使用 OPEN CURSOR FOR EXECUTE,但您不能使用与 Oracle 中相同的结构;在 PL/pgSQL 中,不能将游标声明为字符串替换。这仅在 OPEN 期间完成:http://www.postgresql.org/docs/9.3/static/plpgsql-cursors.html

除了认为为每个用户提供 EAV 设置表不是一个好的设计之外......

【讨论】:

以上是关于PL/pgSQL 代码的问题的主要内容,如果未能解决你的问题,请参考以下文章

如何在 PostgreSQL、PL/pgSQL 上执行匿名代码块切换 CASE 语句?

Pl/Pgsql,将数组参数传递给 INSERT

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

Pl/pgSQL 匿名代码块在 [42601] 错误上失败:查询没有结果数据的目的地

如何在 PL/pgSQL 中获取动态生成的字段名称的值

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