为了便于阅读,我将提供数据库的简化版本。
假设我有一个基表:
CREATE TABLE base_table
(
id bigserial NOT NULL,
name text,
CONSTRAINT requests_pkey PRIMARY KEY (id)
);
继承它的人:
CREATE TABLE child_table
()
INHERITS (base_table);
在基表上设置了一个触发器:
CREATE OR REPLACE FUNCTION insert_row()
RETURNS trigger AS
$BODY$
BEGIN
INSERT INTO child_table VALUES (NEW.*);
RETURN NULL;
END;
$BODY$
LANGUAGE plpgsql;
CREATE TRIGGER row_insert_trigger
BEFORE INSERT
ON base_table
FOR EACH ROW
EXECUTE PROCEDURE insert_row();
我的数据库中此类触发器的目的是根据某些规则对基表进行分区。它按预期工作,但它有一个显着的缺点。这样INSERT
:
INSERT INTO base_table (name) VALUES ('test') RETURNING id
将插入一行,但由于触发器函数返回NULL
,它不会返回任何内容。我尝试了几个解决这个问题的方法,但是,坦率地说,我对它们都不满意:
上述问题的解决方案可能是一个简单的规则,例如:
CREATE OR REPLACE RULE row_insert_rule AS
ON INSERT TO base_table DO INSTEAD
INSERT INTO child_table VALUES (NEW.*)
RETURNING id;
然而,在现实生活中,我想执行比这个简单插入更多的操作,这需要使用存储过程。我不知道如何在返回的规则中使用它:
CREATE OR REPLACE RULE row_insert_rule AS
ON INSERT TO base_table DO INSTEAD
SELECT id FROM some_procedure(NEW)
RETURNING id;
显然会失败,因为使用SELECT
和RETURNING
是无效的pgsql语法。在规则中省略RETURNING
会导致INSERTs
RETURNING
失败。
BEFORE
和AFTER
在此解决方案中,BEFORE
触发器看起来与之前提供的触发器类似,但它不会返回NULL
而是NEW
- 因此它可以与RETURNING
一起使用,但它会插入一行到base_table
。之后可以使用AFTER
触发器删除该行。我认为这个解决方案不够优雅 - 它看起来很奇怪,会导致id上的序列出现问题等等。
INSERT
我可以执行一个存储过程,而不是在INSERT
上使用base_table
语句,该存储过程将在相应的表中插入一行。在我看来,这个解决方案在性能方面是最好的,但它需要使用这个数据库的应用程序“记住”不要在这个表上使用INSERT
并依赖提供的存储过程 - 这是我能要求的因为我们系统的架构而无法实现。
与上一个解决方案中的问题相同 - 要求应用程序执行的操作不仅仅是向表中插入数据。
是否有一个简单,优雅,最重要的 - 快速(在性能方面)解决我的问题?或者 - 可以修改我的解决方案1号以正确使用RETURNING
吗?