web-dev-qa-db-ja.com

2つのテーブルのトリガー関数でのSELECT

Gcc(Debian 4.9.2-10)4.9.2、64ビットでコンパイルされたx86_64-unknown-linux-gnu上のPostgreSQL 9.4.3の私のテーブルとトリガー:

_CREATE TABLE measurements (
        measurement_id SERIAL PRIMARY KEY NOT NULL,
        measurement_size_in_bytes INTEGER NOT NULL
    );    

CREATE TABLE file_headers (
        header_id SERIAL PRIMARY KEY NOT NULL, 
        measurement_id INTEGER NOT NULL, 
        file_header_index_start INTEGER,
        file_header_index_end INTEGER
    );

CREATE TRIGGER measurement_ids AFTER INSERT 
        ON measurements FOR EACH ROW 
        EXECUTE PROCEDURE ins_function('SELECT measurement_id FROM measurements 
        ORDER BY measurement_id desc limit 1;', 1, 666 ); 
_

ここで、SERIAL以降、SELECTのデータ型はINTEGERであると想定しましたが、トリガーを開始する次のコマンドからエラーメッセージが表示されるため、明らかにfalseです。

_INSERT INTO measurements (measurement_size_in_bytes) VALUES (888);` 
_
_ERROR:  invalid input syntax for integer: "SELECT measurement_id FROM measurements ORDER BY measurement_id desc limit 1;"
CONTEXT:  PL/pgSQL function ins_function() line 10 at assignment
_

編集する

ins_function()および@a_horse_with_no_nameと@Joishiのコメントに基づく編集:

_CREATE OR REPLACE FUNCTION ins_function() RETURNS TRIGGER AS $$
    --
    -- Perform AFTER INSERT operation on file_header by creating rows with new.measurement_id, new.file_header_index_start and new.file_header_index_end.
    --
DECLARE
    measurement_id              INTEGER;
    file_header_index_start     INTEGER;
    file_header_index_end       INTEGER; 
BEGIN     

    SELECT a.measurement_id INTO measurement_id from measurements a ORDER BY measurement_id desc limit 1;
    file_header_index_start := TG_ARGV[0];
    file_header_index_end := TG_ARGV[1]; 

    IF TG_OP = 'INSERT' THEN
        INSERT INTO file_headers (measurement_id, file_header_index_start, file_header_index_end)
        VALUES (measurement_id, file_header_index_start, file_header_index_end); 
        RETURN NEW; 
    END IF;

    RETURN NULL; -- result is ignored since this is an AFTER trigger
END;
$$ LANGUAGE plpgsql;

--
-- Function and trigger on INSERT. 
--
CREATE TRIGGER measurement_ids AFTER INSERT 
    ON measurements FOR EACH ROW EXECUTE PROCEDURE ins_function(1, 666); 
_

エラーは発生しませんが、出力は間違っています。テーブルで正常に表示されているのにINSERTがテーブル_file_headers_に表示されていませんmeasurements

@ErwinBrandstetterの回答の出力

ですから、TEXTからINTへのキャストについて考え始めましたが、_TG_ARGV[]_はテキストのデータ型なので、これは基本的な操作になるはずです。失敗した試みの1つはformat('SELECT $1.%I', TG_ARGV[0])です。 regclassinsaft_function()の-​​ here の説明どおりに機能します

_SELECT NEW.measurement_id, TG_ARGV[0]::regclass, TG_ARGV[1]::regclass;
_

_file_headers_テーブルへのINSERTが成功しないのはなぜですか?

未解決の名前の競合があります

宣言せずに古いバージョンのPostgresを使用している必要があります。または、デフォルト以外の構成設定で操作しています。

ここでは、_measurement_id_という名前の変数を宣言します。

_DECLARE
    measurement_id              INTEGER;
_

あいまいな変数名を最初から使用するのはfollyです。とにかくそれをするなら、あなたはあなたがしていることを知っている必要があります。 __measurement_id_のように、列名とは異なり、変数名の先頭にアンダースコアを付けるのは慣れさせています。

後のSELECTステートメントはあいまいです:

_ORDER BY measurement_id
_

これにより、デフォルトの設定で最新のPostgreSQLにエラーメッセージが表示されます。 ドキュメントごと

デフォルトでは、PL/pgSQLはSQLステートメントの名前が変数またはテーブルの列を参照できる場合、エラーを報告します。

そして:

システム全体でこの動作を変更するには、構成パラメーター_plpgsql.variable_conflict_をerror、use_variable、またはuse_column(errorは出荷時のデフォルト)のいずれかに設定します。このパラメータは、PL/pgSQL関数内のステートメントの後続のコンパイルに影響しますが、現在のセッションですでにコンパイルされているステートメントには影響しません。この設定を変更すると、PL/pgSQL関数の動作に予期しない変更が発生する可能性があるため、スーパーユーザーのみが変更できます。

9.0より古いPostgresでは、これは変数を意味するように解決されます。 ドキュメントごと

そのような場合、PL/pgSQLがあいまいな参照を変数として解決するように指定できます(これは、PL/pgSQLの動作PostgreSQL 9.0より前

大胆な強調鉱山。
これにより、任意の結果になります。これは、ソート順が未定であるためです。

監査された機能

_CREATE OR REPLACE FUNCTION insaft_function()
   RETURNS TRIGGER AS
$func$
DECLARE
   _measurement_id          integer;
   _file_header_index_start integer := TG_ARGV[0]::int;
   _file_header_index_end   integer := TG_ARGV[1]::int; 
BEGIN     

   SELECT a.measurement_id   INTO _measurement_id
   FROM   measurements a
   ORDER  BY a.measurement_id DESC  -- you had ambiguity here!
   LIMIT  1;

   IF TG_OP = 'INSERT' THEN  -- noise if only used in AFTER INSERT trigger
      INSERT INTO file_headers (measurement_id, file_header_index_start
                                              , file_header_index_end)
      VALUES (_measurement_id, _file_header_index_start, _file_header_index_end); 
   END IF;

   RETURN NULL; -- result is ignored since this is an AFTER trigger
END
$func$ LANGUAGE plpgsql;
_

これは_AFTER INSERT_トリガーでのみ使用されるため、どのようにinsaft_function()と名付けたかに注意してください。

引き金:

_CREATE TRIGGER insaft_measurement_ids
AFTER INSERT ON measurements
FOR EACH ROW EXECUTE PROCEDURE insaft_function(1, 666);
_

しかし、提供された設定では、関数を大幅に簡略化できます。

_CREATE OR REPLACE FUNCTION insaft_function()
   RETURNS TRIGGER AS
$func$
BEGIN     
   INSERT INTO file_headers (measurement_id, file_header_index_start
                                           , file_header_index_end)
   VALUES (NEW.measurement_id, TG_ARGV[0]::int, TG_ARGV[1]::int);

   RETURN NULL;  -- result ignored since this is an AFTER trigger
END
$func$ LANGUAGE plpgsql;
_
6