我有几个表格,它们使用触发器相互交互,我目前处理触发器执行的方式是使用
我真的希望最终触发器只运行一次,并且在所有逐行操作发生后才运行。不幸的是,
我查看了该主题周围的其他 SO 问题,没有找到类似于我所做的东西。
以下是设置:
因此,我希望能够完成以下设置:
pg_trigger_depth() < 2
,这种方法不够优美。我真的希望最终触发器只运行一次,并且在所有逐行操作发生后才运行。不幸的是,
CONSTRAINT TRIGGER
只支持 FOR EACH ROW
,而 FOR STATEMENT
触发器实际上会在触发器中的每个语句中触发一次,而不仅仅是在启动它的初始语句中触发一次。我查看了该主题周围的其他 SO 问题,没有找到类似于我所做的东西。
以下是设置:
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();
因此,我希望能够完成以下设置:
- 插入报告记录
- 确保报告名称在任何单一时间点上只能存在一次(使用valid_range上的EXCLUDE)
- 在订阅者表中插入报告订阅者
- 确保一个订阅者同时只能订阅一个报告。
- 允许多个人订阅报告。
- 每当向订阅者表中添加记录时,将该名称添加到报告表中的订阅者列表中。
- 每当从订阅者表中删除记录时,从报告表中的订阅者列表中删除名称。
- 每当从报告表中删除记录时,删除相应的订阅者记录(由ON DELETE CASCADE处理)
如果在单个语句中对订阅者表进行了大量编辑(常见情况),最好只运行一个简单的查询来使用订阅者表中新记录和剩余记录的聚合更新报告表。
我的原始解决方案涉及向订阅者表添加needs_update标志,并基于此触发更新然后关闭该标志。 当然,这会导致触发器的另一个触发,我使用pg_trigger_depth() < 2
(2是因为插入可能是由系统中的其他触发器引起的)来停止该触发。除了丑陋之外,还很烦人的是触发器函数中的语句会导致更多的FOR EACH STATEMENT
触发。
我尝试了使用标志的不同版本,其中包含我在其他SO答案中看到的技巧(https://dev59.com/dWox5IYBdhLWcg3w1Xy-#8950639),即创建一个临时表并捕获重复表异常以防止进一步执行。 不过,我认为这并没有真正改善问题。
有没有一种干净的方法来完成我正在尝试做的事情?虽然这是一个明显的玩具示例,但我的实际应用程序确实需要构建数据的“压缩数组”表示,并且最好以有效的方式进行。
SQL_DROP
上创建一个EVENT TRIGGER
,希望在ON COMMIT DROP
删除临时表后会触发该事件,但是触发器并不会对自动删除的操作进行响应,只有显式删除才会触发。 - deinspanjerneeds_sync
标记是否还有其他用途,或者只是为了这个触发器而存在? - Nick Barnes