0

我有以下 plpgsql 程序;

DECLARE 
     _r record;
     point varchar[] := '{}';
     i int := 0;

BEGIN



FOR _r IN EXECUTE ' SELECT a.'|| quote_ident(column) || ' AS point,
       FROM ' || quote_ident (table) ||' AS a'
LOOP

       point[i] = _r;
       i = i+1;

END LOOP;

RETURN 'OK';
END;

它的主要目标是遍历一个表并将行的每个值存储在一个数组中。我还是 plpgsql 的新手。谁能指出错误,因为它给了我以下错误; 在此处输入图像描述

4

2 回答 2

1

这是完整的语法(请注意,我将参数重命名columncol_nameascolumn保留字。同样适用于table

create or replace function foo(col_name text, table_name text)
  returns text
as
$body$

DECLARE 
     _r record;
     point character varying[] := '{}';
     i int := 0;

BEGIN
    FOR _r IN EXECUTE 'SELECT a.'|| quote_ident(col_name) || ' AS pt, FROM ' || quote_ident (table_name) ||' AS a'
    loop
     point[i] = _r;
     i = i+1;
   END LOOP;

   RETURN 'OK';
END;
$body$
language plpgsql;

虽然说实话:我失败了​​,所以看看你在这里想要达到的目标。

于 2013-02-19T09:51:52.700 回答
0

@a_horse解决了您尝试失败的大部分严重问题。

但是,没有人应该使用这个。以下分步说明应该会导致使用现代 PostgreSQL 进行合理的实施。

阶段 1:消除错误和恶作剧

  • 删除SELECT列表后的逗号以修复语法错误。

  • 您以 开始数组0,而默认以 开始1。只有在需要时才这样做。如果您使用array_upper()et al. 操作会导致意想不到的结果。开始1吧。

  • RETURN将类型更改为varchar[]以返回组装的数组并使此演示有用。

到目前为止我们有什么:

CREATE OR REPLACE FUNCTION foo(tbl varchar, col varchar)
  RETURNS varchar[] LANGUAGE plpgsql AS
$BODY$
DECLARE 
   _r record;
   points varchar[] := '{}';
   i int := 0;
BEGIN
   FOR _r IN
      EXECUTE 'SELECT a.'|| quote_ident(col) || ' AS pt
      FROM ' || quote_ident (tbl) ||' AS a'
   LOOP
      i = i + 1;        -- reversed order to make array start with 1
      points[i] = _r;
   END LOOP;

   RETURN points;
END;
$BODY$;

第 2 阶段:去除杂物,使其有用

  • 为简单起见,使用/text代替。不过,两者都有效。character varyingvarchar

  • 您正在选择单个列,但使用类型的变量record。这样,整个记录被强制转换为text,其中包括括号。几乎没有任何意义。请改用text变量。如果您显式转换为( ) ,则适用于任何列。任何类型都可以转换为.text::texttext

  • 初始化变量没有意义point。它可以从NULL这里开始。

  • 在这种情况下,内部的表和列别名EXECUTE是没有用的。动态执行的 SQL 有自己的作用域!

  • 在 plpgsql 函数的;final 之后不需要分号 ( )。END

  • 使用 .将每个值附加到数组中更简单||

几乎是理智的:

CREATE OR REPLACE FUNCTION foo1(tbl text, col text)
  RETURNS text[] LANGUAGE plpgsql AS
$func$
DECLARE 
   point  text;
   points text[];
BEGIN
   FOR point IN
      EXECUTE 'SELECT '|| quote_ident(col) || '::text FROM ' || quote_ident(tbl)
   LOOP
      points = points || point;
   END LOOP;

   RETURN points;
END
$func$;

第 3 阶段:让它在现代 PL/pgSQL 中大放异彩

  • 如果将表名作为 传递text,则会造成模棱两可的情况。您可以使用or很好地阻止SQLi,但是对于您之外的表,这将失败。 然后您需要添加schema-qualification,这会创建一个模棱两可的值。'xy' 可以代表表名“xy”或模式限定的表名“x”.“y”。你不能传递 "x"."y" 因为它会被转义到 """x"".""y"""。您需要为模式名称使用​​附加参数,或者在强制使用时根据需要自动引用一个类型的参数,这是这里的优雅解决方案。format()quote_ident()search_path
    regclass regclasstext

  • newformat()比多个(甚至单个)quote_ident()调用更简单。

  • 您没有指定任何订单SELECT以任意顺序返回行,不带ORDER BY. 这可能看起来很稳定,因为只要基础表不改变,结果通常是可重现的。但这是 100% 不可靠的。您可能想要添加某种ORDER BY.

  • 最后,您根本不需要循环。SELECT使用带有Array 构造函数的纯文本。

  • 使用OUT参数进一步简化代码

适当的解决方案:

CREATE OR REPLACE FUNCTION f_arr(tbl regclass, col text, OUT arr text[])
 LANGUAGE plpgsql AS
$func$
BEGIN

EXECUTE format('SELECT ARRAY(SELECT %I::text FROM %s ORDER BY 1)', col, tbl)
INTO arr;

END
$func$;

称呼:

SELECT f_arr('myschema.mytbl', 'mycol');
于 2013-03-10T17:48:48.630 回答