意外的SQL结果:字符串与直接SQL

时间:2011-05-29 03:47:32

标签: postgresql plpgsql executequery

使用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

谢谢。

2 个答案:

答案 0 :(得分: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函数的一部分,所以很难说),但我的猜测是你遇到了上述问题的变异。

答案 1 :(得分:1)

来自Tom Lane:

  

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

     

注意:如果此类型存在歧义,PG 9.0将默认抛出错误。