PL / pgSQL函数的SQL语句中的语法错误

时间:2012-07-23 14:57:58

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

我在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;

任何人都可以理解为什么我会收到这些错误 - 我该如何修复它们?

1 个答案:

答案 0 :(得分: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列表项),或者它可以是由...形成的任意表达式   输入列值。