【问题标题】:plpgsql function issueplpgsql函数问题
【发布时间】:2013-02-03 22:03:27
【问题描述】:

我有以下 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 的新手。谁能指出错误,因为它给了我以下错误;

【问题讨论】:

  • 您缺少create function 部分。你需要引用函数的主体,例如使用美元报价。详情请阅读手册。
  • 我正在使用 postgis 的 GUI 来创建函数,所以不需要在函数代码中执行“创建函数”,因为它是自动完成的。 @a_horse_with_no_name
  • @IT_info “PostGIS 的 GUI”是指 PgAdmin-III 吗?这就是屏​​幕截图所暗示的。如果是这样,您能否逐步解释您如何尝试以这种方式创建函数?在我看来,周围的CREATE [OR REPLACE] FUNCTION 块确实没有被发送。也许看看底层的查询日志?
  • 问题是我将 sql 作为一种语言而不是 plpgsql 工作。感谢您的帮助
  • 您还缺少loopend loop 来定义for 循环的范围。你也不能像你一样动态地使用表名或列名。

标签: sql postgresql postgis plpgsql


【解决方案1】:

这是完整的语法(请注意,我将参数column 重命名为col_name,因为column 是保留字。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;

虽然说实话:我失败了​​,所以看看你在这里想要实现什么。

【讨论】:

  • 基本上我想要的是将所有点放在数组中,以便通过使用 google map api 我得到所有点的高度。这是正确的方法还是我采用了错误的方法? @a_horse_with_no_name
  • @IT_info:如果你想用这些值“做”一些事情,你将需要更多的代码。如果您希望这些值作为函数的结果,则需要更改函数的返回类型。
  • 我知道我必须将返回类型设置为数组,对吗? @a_horse_with_no_name。但这个概念是正确的。是否可以从 javascript 端读取每个值以获取每个点的高度?
【解决方案2】:

@a_horse 修复了大部分与您的失败尝试有关的严重问题。

但是,任何人都不应使用它。以下分步说明应该会导致使用现代 PostgreSQL 进行合理的实施。

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

  • 去掉SELECT列表后面的逗号,修复语法错误。

  • 您的阵列以0 开头,而默认以1 开头。只有在需要时才这样做。如果您使用array_upper() 等人操作会导致意外结果。请以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 varying / varchar。不过,两者都可以。

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

  • 初始化变量point 没有意义。它可以在此处以NULL 开头。

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

  • 在 plpgsql 函数中的最终 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 传递,则会造成模棱两可的情况。您可以使用format()quote_ident() 来阻止SQLi,但是对于search_path 之外的表,这将失败。
    然后您需要添加 schema-qualification,这会创建一个模棱两可的值。 “x.y”可以代表表名“x.y”或模式限定的表名“x”.“y”。你不能传递 "x"."y" 因为它会被转义到 """x"".""y"""。您需要为架构名称使用附加参数,或者在强制转换为 text 时自动引用 regclass regclass 类型的参数,这是这里的优雅解决方案。

  • 新的 format() 比多个(甚至单个)quote_ident() 调用更简单。

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

  • 最后,您根本不需要循环。使用普通的 SELECTArray constructor

  • 使用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');

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-06
    • 2017-07-28
    • 2012-03-26
    • 1970-01-01
    • 1970-01-01
    • 2013-02-20
    • 1970-01-01
    • 2015-04-05
    相关资源
    最近更新 更多