【问题标题】:Syntax error in SQL statement of PL/pgSQL functionPL/pgSQL 函数的 SQL 语句中的语法错误
【发布时间】:2012-07-23 14:57:57
【问题描述】:

我在 SO 社区的帮助下编写了一个存储过程。我拼凑/拼凑了各种问题的答案来编写我的函数。

但是,当我尝试在 db (PostgreSQL 8.4) 中创建我的函数时,我收到以下错误:

ERROR:  syntax error at or near "$4"
LINE 1: ...ank() OVER (ORDER BY  $1 ,  $2 ) /  $3 )::int as  $4  , $1 ,...
                                                            ^

QUERY:   SELECT ceil(rank() OVER (ORDER BY  $1 ,  $2 ) /  $3 )::int as  $4  , $1 ,  $2 ,  $5 ,  $6 ,  $7 ,  $8 ,  $9 ,  $10  FROM foobar WHERE  $2  BETWEEN  $11  AND  $12  AND  $1  = ANY( $13 ) ORDER BY  $1 ,  $2 ,  $4 
CONTEXT:  SQL statement in PL/PgSQL function "custom_group" near line 9

这是我要创建的函数的代码:

CREATE OR REPLACE FUNCTION custom_group(start_date DATE, end_date DATE, grouping_period INTEGER, _ids int[] DEFAULT '{}')
RETURNS TABLE (
grp INTEGER,
id INTEGER, 
entry_date DATE,
pop REAL,
hip REAL,
lop REAL,
pcl REAL,
vop BIGINT,
poi BIGINT) AS
$BODY$
BEGIN

IF _ids <> '{}'::int[] THEN -- excludes empty array and NULL
    RETURN QUERY 
                    SELECT ceil(rank() OVER (ORDER BY id, entry_date) / $3)::int as grp
                          ,id, entry_date, pop, hip, lop, pcl, vop, poi
                    FROM   foobar
                    WHERE  entry_date BETWEEN start_date AND end_date AND id = ANY(_ids)
                    ORDER  BY id, entry_date, grp ;

ELSE
    RETURN QUERY 
                    SELECT ceil(rank() OVER (ORDER BY id, entry_date) / $3)::int as grp
                          ,id, entry_date, pop, hip, lop, pcl, vop, poi
                    FROM   foobar
                    WHERE  entry_date BETWEEN start_date AND end_date
                    ORDER  BY id, entry_date, grp ;

END IF;

END;
$BODY$ LANGUAGE plpgsql;

谁能理解我为什么会收到这些错误 - 以及如何解决这些错误?

【问题讨论】:

  • 我碰巧记得我过去曾遇到过这样神秘的错误消息,我设法通过为输出表使用不同的字段名称来解决这个问题。这次我应用了相同的“hack”,并且能够创建没有错误的函数。但是,由于我似乎无法在文档中找到这种行为,我猜这是一种黑客行为——我会等到真正知道这种奇怪行为原因的人为我指出正确的方向。

标签: sql postgresql stored-procedures naming-conventions plpgsql


【解决方案1】:

错误来自命名冲突

变量grpRETURNS TABLE 子句隐式定义。在函数体中,你尝试使用与列别名相同的标识符,这会发生冲突。

只需为 grp 使用不同的名称 - 无论如何,列别名在函数之外将不可见。

以及table-qualify其他列:

CREATE OR REPLACE FUNCTION custom_group(_start_date DATE
                                       ,_end_date DATE
                                       ,_grouping_period INTEGER, 
                                       ,_ids int[] DEFAULT '{}')
RETURNS TABLE (grp int, id int, entry_date date, pop real, hip real,
               lop real, pcl real, vop bigint, poi bigint) AS
$BODY$
BEGIN

IF _ids <> '{}'::int[] THEN -- excludes empty array and NULL
    RETURN QUERY 
    SELECT ceil(rank() OVER (ORDER BY f.id, f.entry_date) / $3)::int AS _grp
          ,f.id, f.entry_date, f.pop, f.hip, f.lop, f.pcl, f.vop, f.poi
    FROM   foobar f
    WHERE  f.entry_date BETWEEN _start_date AND _end_date AND id = ANY(_ids)
    ORDER  BY f.id, f.entry_date, _grp;

ELSE
    RETURN QUERY 
    SELECT ceil(rank() OVER (ORDER BY f.id, f.entry_date) / $3)::int -- no alias
          ,f.id, f.entry_date, f.pop, f.hip, f.lop, f.pcl, f.vop, f.poi
    FROM   foobar f
    WHERE  f.entry_date BETWEEN _start_date AND _end_date
    ORDER  BY f.id, f.entry_date, 1; -- ordinal pos. instead of col alias
END IF;

END;
$BODY$ LANGUAGE plpgsql;

我在IN参数前加上_的原因是一样的:避免这样的命名冲突。

在这种情况下,您甚至根本不必为计算列使用别名。您可以使用ORDER BY 中的序数位置,就像我在第二个查询中演示的那样。我引用手册here

每个表达式可以是输出列的名称或序号 (SELECT 列表项),或者它可以是由以下组成的任意表达式 输入列值。

【讨论】:

  • +1 提示在入站参数前导下划线以避免命名冲突。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-16
  • 1970-01-01
  • 2022-01-09
  • 1970-01-01
  • 1970-01-01
  • 2016-01-21
  • 1970-01-01
相关资源
最近更新 更多