web-dev-qa-db-ja.com

"エラー:" EXECUTE .. USING ..; "にパラメータ$ 1はありません。 plpgsqlのステートメント

次のように、PostgreSQLのテーブル継承を使用して子テーブルを作成するplpgsql関数があります。

CREATE TABLE parent_table (
  value integer,
  end_time timestamp without time zone
);

CREATE OR REPLACE FUNCTION mk_child(_year INTEGER, _month INTEGER)
RETURNS text AS $$
DECLARE
  tname varchar;
  start_date date;
  end_date date;
  next_month varchar := (_month + 1)::text;
  next_year varchar := (_year + 1)::text;
BEGIN
  tname := 'child_y' || substring(_year::text from 3 for 2)
           || 'm' || lpad(_month::text, 2, '0');
  start_date := DATE (_year::text || '-' || _month::text || '-01');
  IF ( _month = 12 ) THEN
       end_date := DATE (next_year || '-01-01');
  ELSE
       end_date := DATE (_year::text || '-' || next_month || '-01');
  END IF;

  RAISE NOTICE 'Creating child table %', tname;
  EXECUTE format('CREATE TABLE %I ( CHECK ( end_time >= %L AND end_time < %L ) ) 
                  INHERITS (parent_table)',
                 tname, start_date, end_date);
  -- EXECUTE format('CREATE TABLE %I ( CHECK ( end_time >= $1 AND end_time < $2 ) ) 
  --                 INHERITS (parent_table)',
  --                tname)
  --     USING start_date, end_date;
  RETURN tname;
END
$$ LANGUAGE plpgsql;

私がそれを呼ぶとき、それは成功しています:

# select mk_child(2015,1);                                           
NOTICE:  Creating child table child_y15m01
   mk_child   
--------------
 child_y15m01
(1 row)

ただし、EXECUTE ... USING ...;フォーム(上記のスニペットでコメント化)、エラーが発生します:

# select mk_child(2015,2);
NOTICE:  Creating child table child_y15m02
ERROR:  there is no parameter $1
CONTEXT:  SQL statement "CREATE TABLE child_y15m02 ( CHECK ( end_time >= $1 AND end_time < $2 ) ) INHERITS (parent_table)"
PL/pgSQL function mk_child(integer,integer) line 21 at EXECUTE statement

PostgreSQL docs この形式が機能し、より効率的であることを明示的に述べます。

では、なぜ期待どおりに機能しないのでしょうか。

3
donatello

値はDMLステートメントにのみ渡すことができます。 マニュアルの説明:

パラメータシンボルのもう1つの制限は、SELECTINSERTUPDATE、およびDELETEコマンドでのみ機能することです。他のステートメントタイプ(一般的にユーティリティステートメントと呼ばれます)では、値が単なるデータ値であっても、テキストで値を挿入する必要があります。

そう CREATE TABLEステートメントは、USING制約lookのような式がパラメーター化されている可能性のある値であっても、CHECK句を介したパラメーターを受け入れません。

それはさておき、関数を大幅に簡略化できます。

CREATE OR REPLACE FUNCTION mk_child(_year int, _month int)
  RETURNS text AS
$func$
DECLARE
   start_date date := to_date(_year::text || _month::text, 'YYYYMM');
   tname text := to_char(start_date , '"child_y"YY"m"MM');
BEGIN
   RAISE NOTICE 'Creating child table %', tname;
   EXECUTE format('
      CREATE TABLE %I (CHECK (end_time >= %L AND end_time < %L))
      INHERITS (parent_table)'
    , tname, start_date, (start_date + interval '1 month')::date);
   RETURN tname;
END
$func$ LANGUAGE plpgsql;

関連:

これがテーブルのパーティション分割に関するものである場合は、Postgres 10以降の新しい宣言的なテーブルのパーティション分割を参照してください。サンプルコードとリンクを含む関連する回答:

2