2

ワーキングSQL

次のコードは期待どおりに機能し、2 列のデータ (行番号と有効な値) を返します。

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

次のコードは期待どおりに動作しません。最初の列は行番号、2 番目の列は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クエリ自体が 2 つの有効な列を返すのに、機能しないコードが 2 番目の列の値を返すのはなぜですか? (この質問は主に学術的なものです。クエリをテキスト文字列でラップせずに表現する方法があれば、それを知っておくとよいでしょう。)

完全なコード

http://pastebin.com/hgV8f8gL

ソフトウェア

PostgreSQL 8.4

ありがとうございました。

4

2 に答える 2

1

トム・レーン より:

問題は、実際には plpgsql 関数のローカル変数であるときに、「金額」がクエリのテーブル列を参照すると想定していることだと思います。テーブルの名前/エイリアスで列参照を修飾しない限り、2 番目の解釈が優先されます。

注: PG 9.0 は、このタイプのあいまいさが存在する場合、デフォルトでエラーをスローします。

于 2011-05-31T10:10:09.783 に答える
1

2 つのステートメントは厳密には同等ではありません。

id = 4 と仮定すると、最初のものは各パスで計画/準備され、次のように動作します。

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

もう 1 つは最初のパスでのみ計画/準備され、次のように動作します。

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

(ループは実際には上記のカーソルを準備しますが、それは私たちにとって重要なことではありません。)

多くの要因により、2 つの結果が異なる場合があります。

  • プロシージャを呼び出す前の検索パスの変更は、2 回目の呼び出しでは無視されます。特に、これがx_table何か違うことを示している場合。
  • すべての種類の定数と不変関数の呼び出しは、2 番目の呼び出しの計画に「組み込まれています」。

これをこれらの副作用の実例と考えてください。

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

あなたのケースで2つが同じ結果を返さない理由は、コンテキストによって異なります(pl/pgsql関数の一部しか投稿していないため、わかりにくいです)が、私の推測では、以上のような問題。

于 2011-05-29T09:59:34.770 に答える