1

PostgreSQL でトリガーを使用する方法を学んでいますが、次のコードで問題が発生します。

CREATE OR REPLACE FUNCTION checkAdressen() RETURNS  TRIGGER AS $$
DECLARE
  adrCnt int = 0;
BEGIN
  SELECT INTO adrCnt count(*) FROM Adresse
  WHERE gehoert_zu = NEW.kundenId;

  IF adrCnt < 1 OR adrCnt > 3 THEN
    RAISE EXCEPTION 'Customer must have 1 to 3 addresses.';
  ELSE
    RAISE EXCEPTION 'No exception';
  END IF;
END;
$$ LANGUAGE plpgsql;

すべてのテーブルを新しく作成した後、この手順でトリガーを作成して、すべて空にします。ただし、count(*)上記のコードの関数は 1 を返しますSELECT count(*) FROM adresse;。PL/pgSQL の外部で実行すると、0 が返されます。FOUND変数を使用してみましたが、常に true です。

さらに奇妙なことに、いくつかの値をテーブルに挿入し、それらを再度削除して再び空にすると、コードは意図したとおりに機能し、0count(*)を返します。なしよりも句。WHERE gehoert_zu = NEW.kundenIdcount(*)WHERE

- 編集:

手順の使用方法の例を次に示します。

CREATE TABLE kunde (
kundenId    int PRIMARY KEY
);

CREATE TABLE adresse (
id      int PRIMARY KEY,
gehoert_zu  int REFERENCES kunde
);

CREATE CONSTRAINT TRIGGER adressenKonsistenzTrigger AFTER INSERT ON Kunde
DEFERRABLE INITIALLY DEFERRED
FOR EACH ROW
EXECUTE PROCEDURE checkAdressen();

INSERT INTO kunde VALUES (1);
INSERT INTO adresse VALUES (1,1);

DEFERRABLE INITIALLY DEFERREDパーツを間違えているようです。トリガーは最初のINSERTステートメントの後に実行されると想定しましたが、挿入はブロック内にありませBEGIN;んが、2番目のステートメントの後に発生します。COMMIT;

PostgreSQL のドキュメントによると、そのようなブロック内にない場合、挿入は毎回自動的にコミットされるためadresse、最初のINSERTステートメントがコミットされたときにエントリが存在しないはずです。

誰かが私の間違いを指摘できますか?

- 編集:

トリガーDEFERRABLE INITIALLY DEFERREDは問題なく動作しているようです。私の間違いは、BEGIN- COMMIT-Block を使用していないため、各挿入が独自のトランザクションで実行され、トリガーが毎回実行されると仮定することでした。

ただし、BEGIN-がなくても、COMMITすべての挿入が 1 つのトランザクションにまとめられ、その後トリガーが実行されます。この動作を考えると、BEGIN-を使用するポイントは何COMMITですか?

4

2 に答える 2

0

BEFOREこれは少しばかげているように思えるかもしれませんが、答えは、トリガーの延期をやめて挿入を実行する必要があるということです。挿入後に実行すると、もちろんテーブルにデータがあります。

私が知る限り、これは期待どおりに機能しています。

もう 1 つ注意してください。

RAISE EXCEPTION 'No Exception';

あなたはおそらくしたいです

RAISE INFO 'No Exception';

次に、設定を変更し、トランザクションでクエリを実行して、トリガーが意図したとおりに動作することをテストできます。そのままでは、すべての挿入が失敗し、手順を編集せずにこれを本番環境に移行する方法はありません.

于 2013-09-01T03:46:54.107 に答える