2

工作 SQL

以下代码按预期工作,返回两列数据(行号和有效值):

sql_amounts := '
  SELECT
    row_number() OVER (ORDER BY taken)::integer,
    avg( amount )::double precision
  FROM
    x_function( '|| id || ', 25 ) ca,
    x_table m
  WHERE
    m.category_id = 1 AND
    m.location_id = ca.id AND
    extract( month from m.taken ) = 1 AND
    extract( day from m.taken ) = 1
  GROUP BY
    m.taken
  ORDER BY
    m.taken';

FOR r, amount IN EXECUTE sql_amounts LOOP
  SELECT array_append( v_row, r::integer ) INTO v_row;
  SELECT array_append( v_amount, amount::double precision ) INTO v_amount;
END LOOP;

非工作 SQL

以下代码无法按预期工作;第一列是行号,第二列是NULL.

FOR r, amount IN
  SELECT
    row_number() OVER (ORDER BY taken)::integer,
    avg( amount )::double precision
  FROM
    x_function( id, 25 ) ca,
    x_table m
  WHERE
    m.category_id = 1 AND
    m.location_id = ca.id AND
    extract( month from m.taken ) = 1 AND
    extract( day from m.taken ) = 1
  GROUP BY
    m.taken
  ORDER BY
    m.taken
LOOP
  SELECT array_append( v_row, r::integer ) INTO v_row;
  SELECT array_append( v_amount, amount::double precision ) INTO v_amount;
END LOOP;

问题

当查询本身返回两个有效列时,为什么非工作代码会返回NULL第二列的值?(这个问题主要是学术性的;如果有一种方法可以表达查询而无需将其包装在文本字符串中,那将很高兴知道。)

完整代码

http://pastebin.com/hgV8f8gL

软件

PostgreSQL 8.4

谢谢你。

4

2 回答 2

1

这两个语句不是严格等价的。

假设 id = 4,第一个在每次通过时都计划/准备好,其行为类似于:

prepare dyn_stmt as '... x_function( 4, 25 ) ...'; execute dyn_stmt;

另一个仅在第一次通过时计划/准备,并且行为更像:

prepare stc_stmt as '... x_function( $1, 25 ) ...'; execute stc_stmt(4);

(该循环实际上会使其为上述内容准备一个光标,但为了我们的利益,这不是重点。)

许多因素可以使两者产生不同的结果。

  • 第二次调用将忽略调用过程之前的搜索路径更改。特别是如果这x_table表明一些不同的东西。
  • 在第二次调用的计划中,各种常量和对不可变函数的调用都是“硬连线”的。

将此视为这些副作用的说明:

deallocate all;
begin;
prepare good as select now();
prepare bad as select current_timestamp;
execute good; -- yields the current timestamp
execute bad;  -- yields the current timestamp
commit;
execute good; -- yields the current timestamp
execute bad;  -- yields the timestamp at which it was prepared

为什么在你的情况下两者没有返回相同的结果取决于上下文(你只发布了你的 pl/pgsql 函数的一部分,所以很难说),但我猜你遇到了以上问题。

于 2011-05-29T09:59:34.770 回答
1

从汤姆莱恩:

我认为问题在于您假设“数量”将引用查询的表列,而实际上它是 plpgsql 函数的局部变量。除非您使用表的名称/别名限定列引用,否则第二种解释将优先。

注意:当存在这种类型的歧义时,PG 9.0 默认会抛出错误。

于 2011-05-31T10:10:09.783 回答