私は、トリガーを使ってお互いにやりとりするテーブルをいくつか持っています。私がトリガーの実行を処理している現在の方法は、醜いpg_trigger_depth() < 2
を使用しています。 最終的なトリガーが1回だけ実行され、最後にすべての行の処理が行われた後、残念なことに、CONSTRAINT TRIGGER
はFOR EACH ROW
のみであり、FOR STATEMENT
トリガーはトリガー内の1つのステートメントにつき実際に起動します。起動した最初のステートメントにつき1回だけではありません。更新プログラムのチェーンの終了時にトリガーを起動するにはどうすればよいですか?
私はこのトピックに関する他のいくつかの質問を見てきましたが、私がやっていることに似たものは見つけられませんでした。ここで
はセットアップです:
CREATE TABLE report(
report_tk SERIAL PRIMARY KEY,
report_id UUID NOT NULL,
report_name TEXT NOT NULL,
report_data INT NOT NULL,
report_subscribers TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[],
valid_range TSTZRANGE NOT NULL DEFAULT '(,)',
EXCLUDE USING GIST ((report_id :: TEXT) WITH =, report_name WITH =, valid_range WITH &&)
);
CREATE TABLE report_subscriber(
report_id INT NOT NULL REFERENCES report ON DELETE CASCADE;
subscriber_name TEXT NOT NULL,
needs_sync BOOLEAN NOT NULL DEFAULT TRUE,
EXCLUDE USING GIST (subscriber_name WITH =, valid_range WITH &&)
);
CREATE OR REPLACE FUNCTION sync_subscribers_to_report()
RETURNS TRIGGER LANGUAGE plpgsql SET SEARCH_PATH TO dwh, public AS $$
BEGIN
RAISE INFO 'Running sync to report trigger';
BEGIN
CREATE TEMPORARY TABLE lock_sync_subscribers_to_report(
) ON COMMIT DROP;
RAISE INFO 'syncing to report, stack depth is: %', pg_trigger_depth();
UPDATE report r
SET report_subscribers = x.subscribers
FROM (
SELECT
report_tk
, array_agg(DISTINCT u.subscriber_name ORDER BY u.subscriber_name) AS subscribers
FROM report_subscriber s
WHERE s.report_tk IN (
SELECT DISTINCT report_tk
FROM report_subscriber s2
WHERE s.needs_sync
)
GROUP BY s.report_tk
) x
WHERE r.report_tk = x.report_tk;
RAISE INFO 'turning off sync flag, stack depth is: %', pg_trigger_depth();
UPDATE report_subscriber
SET needs_sync = FALSE
WHERE needs_sync = TRUE;
RETURN NULL;
EXCEPTION WHEN DUPLICATE_TABLE THEN
RAISE INFO 'skipping recursive call, stack depth is: %', pg_trigger_depth();
RETURN NULL;
END;
END;
$$;
CREATE TRIGGER sync_subscribers_to_report
AFTER INSERT OR UPDATE OR DELETE
ON report_subscriber
FOR STATEMENT
EXECUTE PROCEDURE sync_subscribers_to_report();
したがって、この設定で、私のことができるようにしたいと思います:
- は、レポート名だけができることをレポートレコード
- 保証を挿入します任意の時点で1回存在する(有効範囲のEXCLUDE)
- サブスクライバテーブルにレポートサブスクライバを挿入する
- は、加入者が一度に複数のレポートに加入できないことを保証します。
- 複数の人がレポートを購読できるようにします。
- レコードがサブスクライバテーブルに追加されるたびに、その名前をレポートテーブルのサブスクライバリストに追加します。
- レコードがサブスクライバテーブルから削除されるたびに、レポートテーブルのサブスクライバリストからその名前を削除します。単一のステートメント内の加入者テーブルの編集の多くは(一般的なケースがある場合は、レコードがレポートテーブルから削除されるたびに
- は、
ON DELETE CASCADE
により(の世話を対応する加入者レコードを削除します)、単純なクエリを実行して、サブスクライバテーブルの新しいレコードと残りのレコードの集計を使用してレポートテーブルを更新するのが最善の方法です。
私の元のソリューションはサブスクライバテーブルにneeds_update
フラグを追加し、それを更新してからフラグをオフにしてください。もちろん、これは別のトリガを起動させますpg_trigger_depth() < 2
が付いています(この2つは挿入がシステムの他のトリガーによって引き起こされる可能性があるためです)。 醜いことに加えて、トリガー関数のステートメントがさらに多くのエラーを引き起こすことも迷惑になります。
私は、他のSO答え(https://stackoverflow.com/a/8950639/2340769)の中で見たトリックを使って別のバージョンを試しました。一時テーブルを作成し、さらに実行を防ぐためにdupeテーブル例外をキャッチします。私はそれが本当に問題をかなり改善するとは思わない。
私がやっていることをきれいにする方法はありますか?これは明らかなおもちゃの例ですが、私の実際のアプリケーションでは、データの "パックドアレイ"表現を構築する必要があります。効率的な方法で行うことは素晴らしいことです。
:あなたは、それはそれを埋めるために、トラブル価値があると思うなら
はここで、「上のコミット」トリガーのための一般的な概要です。私は一時テーブルが 'ON COMMIT DROP'によって削除された後に起動することを期待して' SQL_DROP'に 'EVENT TRIGGER'を作成しようとしましたが、トリガーはその自動ドロップでは起動しません。 – deinspanjer
'needs_sync'フラグは何か他に必要ですか?それとも、このトリガーのためだけですか? –
トリガーのためだけにあります。 – deinspanjer