1

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

但是,当我尝试在数据库(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;

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

4

1 回答 1

1

该错误来自命名冲突

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

只需使用不同的名称grp- 列别名无论如何都不会在函数外部可见。

并对其他列进行表限定:

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您可以使用我在第二个查询中演示的顺序位置。我在这里引用手册:

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

于 2012-07-23T17:07:30.413 回答