web-dev-qa-db-ja.com

動的SQLを実行する関数をトリガーするパラメーターを渡す

アーウィンによって解決されたこのコードがあります:
plpgsql関数で動的INSERT ON CONFLICT DO UPDATEを実行します

次に、トリガーでトリガー関数_insert_data_func_をパラメーター(_model_cd_、processdate)で呼び出します。

さまざまなモデルがあるため、動的SQLを使用して物事を簡略化しようとしています。

機能:

_CREATE OR REPLACE FUNCTION insert_data_func() 
RETURNS TRIGGER AS 
$func$ 

DECLARE 
 model_cd text;
 processdate text;

BEGIN  
 model_cd := TG_ARGV[0];
 processdate := TG_ARGV[1];

 EXECUTE
  $x$INSERT INTO tb_moldsummary AS t
     SELECT $1.machine                               
          , $1.model
          , split_part(lot, '_', 1)
          , right(lot, position('_' IN lot) * -1)    
          , COUNT(lot)
          , $1.datetimestamp
     FROM   model$x$ || to_char(now(), 'YYYYMM') || '
     WHERE  lot = $1.lot
     AND    machine = $1.machine
     GROUP  BY machine, model, lot
     ON     CONFLICT ON CONSTRAINT tb_summary_unique
     DO     UPDATE
     SET    machine = $1.machine                     
          , totalshots = t.totalshots + 1
          , datetimestamp = $1.datetimestamp'
 USING NEW;                                            
 RETURN NEW; 
END
$func$  
LANGUAGE plpgsql;
_

トリガー:

_DO$$
BEGIN

EXECUTE $x$CREATE TRIGGER insert_data_trigger 
           AFTER INSERT ON modelsample$x$ || to_char(now(), 'YYYYMM') ||    
           ' FOR EACH ROW EXECUTE PROCEDURE 
             insert_data_func' || 
            (''modelsample''' || ',' || to_char(now(), 'YYYYMM') ||  ')';
END
$$;
_

トリガーは、モデルとプロセスの日付パラメーターをトリガー関数insert_data_func()に渡します。しかし、私はこの誤ったメッセージを受け取ります。

エラー:リレーション "model_cd201707"は存在しません
行9:FROM model_cd201707

insert_data_func()関数で「$ x $」を適切に使用する方法は、モデル名が_model_cd_ variableから値を取得しない理由だと思います。

4
tacticz03

エラーメッセージには_relation "model_cd201707" does not exist_と表示されます。 _search_path_ にその名前のテーブルがないと思いますか?あなたの質問にはテーブル名の混乱があります。 _modelsample..._、_model_cd..._、_model..._ _tb_moldsummary_。ここに何かがありません...

それはさておき、渡されたパラメータ値を変数_model_cd_とprocessdateに割り当てた後、それらは(まだ)まったく使用されていません。

渡された2つのパラメーターがテーブル名の作成に使用されると想定すると、次のように機能する可能性があります。

_CREATE OR REPLACE FUNCTION trg_insert_data_func() 
  RETURNS TRIGGER AS 
$func$ 
BEGIN  
   EXECUTE format(
   $x$INSERT INTO tb_moldsummary AS t
      SELECT $1.machine                               
           , $1.model
           , split_part($1.lot, '_', 1)
           , right($1.lot, position('_' IN lot) * -1)    
           , COUNT(*)  -- never null in this query
           , $1.datetimestamp
      FROM   %I
      WHERE  lot = $1.lot
      AND    machine = $1.machine
      GROUP  BY machine, model, lot
      ON     CONFLICT ON CONSTRAINT tb_summary_unique  -- constraint has same name?
      DO     UPDATE
      SET    machine = $1.machine                     
           , totalshots = t.totalshots + 1
           , datetimestamp = $1.datetimestamp
   $x$, TG_ARGV[0]
   )
   USING NEW;                                            
   RETURN NEW; 
END
$func$  
LANGUAGE plpgsql;
_

トリガー(単一の連結パラメーターをテーブル名として渡す):

_DO
$$
BEGIN
   EXECUTE format(
     'CREATE TRIGGER insert_data_trigger 
      AFTER INSERT ON %1$s
      FOR EACH ROW EXECUTE PROCEDURE trg_insert_data_func(%1$L)'
    , 'modelsample' || to_char(now(), 'YYYYMM')
   );
END
$$;
_

SQLステートメントの(引用符で囲まれていない)テーブル名に_%1$s_を使用し、パラメータ(文字列リテラルとして引用符で囲まれている)に_%1$L_を使用していることに注意してください。この場合、既知の正当な識別子には_%I_は必要ありません。詳細は format() のマニュアルを参照してください。

関数とテーブルの名前をスキーマで修飾して、曖昧さをなくしたい場合があります。 _public.modelsample_のように。もっと:

トリガー定義に組み込まれた日付コンポーネントは、作成後は不変であることに注意してください。作成ステートメント自体(およびトリガー関数)のみが「動的」です。

関連:

But 不必要な複雑化のようです。トリガーが呼び出されるテーブルの名前を渡す必要はありません。 一部の特殊変数は、plpgsqlトリガー関数で自動的に使用できます 。特に:_TG_TABLE_SCHEMA_および_TG_TABLE_NAME_。したがって、トリガー関数で動的SQLを使用しますが、トリガー自体は単純で静的にすることができます。

_CREATE OR REPLACE FUNCTION trg_insert_data_func() 
  RETURNS TRIGGER AS 
$func$ 
BEGIN  
   EXECUTE format(
   $x$INSERT INTO tb_moldsummary AS t
      SELECT $1.machine                               
           , $1.model
           , split_part($1.lot, '_', 1)
           , right($1.lot, position('_' IN lot) * -1)    
           , COUNT(*)  -- never null in this query
           , $1.datetimestamp
      FROM   %I.%I     -- !!
      WHERE  lot = $1.lot
      AND    machine = $1.machine
      GROUP  BY machine, model, lot
      ON     CONFLICT ON CONSTRAINT tb_summary_unique  -- constraint has same name?
      DO     UPDATE
      SET    machine = $1.machine                     
           , totalshots = t.totalshots + 1
           , datetimestamp = $1.datetimestamp
   $x$, TG_TABLE_SCHEMA, TG_TABLE_NAME   -- !!
   )
   USING NEW;                                            
   RETURN NEW; 
END
$func$  
LANGUAGE plpgsql;
_

トリガー自体にはパラメーターがありません。

3