我对数据库触发器和/或视图不太熟悉。我目前正在使用 PostgreSQL 和 HSQL;虽然数据库并不是太重要。我只是想知道任何数据库是否提供这样的东西:
我有一个像这样的(示例)表:
CREATE TABLE DUMMY_TABLE (ID INTEGER, NUMBER INTEGER);
我创建了一个这样的视图:
CREATE VIEW DUMMY_VIEW AS SELECT * FROM DUMMY_TABLE WHERE NUMBER > 5;
我插入了几个实体:
INSERT INTO DUMMY_TABLE VALUES(1,2);
INSERT INTO DUMMY_TABLE VALUES(1,10);
所以当我打电话时,DUMMY_VIEW 只包含 VALUES(1,10)
SELECT * FROM DUMMY_VIEW
所以现在我想做的是向 DUMMY_VIEW 添加一个触发器,每当插入 NUMBER > 5 的实体时都会调用该触发器。
我尝试过将触发器直接添加到 HSQL 和 PostgreSQL 中的 DUMMY_VIEW 中;但他们说触发器不能添加到视图中。
这(或功能类似的解决方案)可能吗?
需要注意的是,PostgreSQL 9.1+ 支持视图上的触发器。请参阅等待 9.1 – 视图上的触发 对此进行简要介绍。
是的,触发器不能直接放置在视图上。您应该做的是在基表上放置一个触发器并检查新的 NUMBER 行的值是否大于 5。
注意:视图只是一个存储的选择语句,因此它并不真正保存数据。这就是为什么人们无法检查视图结构中是否正在插入、删除或更新数据。
我认为你必须将触发器放在桌子上,而不是视图上。
触发器可以在视图上使用查询,以便您DRY。
还有其他原因需要触发器位于视图而不是表上吗?
回复评论的示例
-- Create function
CREATE FUNCTION doWhatIwant() RETURNS trigger AS '
BEGIN
IF NEW.number > 5 THEN
do_stuff
END IF;
RETURN NEW;
END;
' LANGUAGE plpgsql;
-- Create trigger
CREATE TRIGGER yourTrigger AFTER INSERT ON dummy_table
FOR EACH ROW EXECUTE PROCEDURE doWhatIwant();
我不确定你想达到什么目的。
触发器在数据更改时执行代码。视图是(比方说)“可调用的数据子集”。它实际上是不存在的,除非你从中选择。它不能包含触发器,因为它不包含任何内容。
所以基本上你需要在基表上有一个触发器。
如果您将触发器添加到与视图具有相同条件的表中,这是可能的。
扳机主体应具有类似以下内容:
if (inserted.NUMBER > 5) {
do something;
}
//do nothing if inserted.NUMBER is not > 5
HSQLDB 2.x 同时支持可更新视图和触发可更新视图。
您的视图示例可以自行更新。因此,您可以使用视图而不是表来插入/删除/更新行。这将不允许包含 NUMBER <= 5 in inserts and updates.
的行您还可以在视图上定义触发器。这些触发器是用 INSTEAD OF INSERT、INSTEAD OF UPDATE 或 INSTEAD OF DELETE 定义的。在触发器的主体中,您可以检查值并引发无效输入的异常,或将行插入基表中。
例如,您创建
person
表,然后向其中插入 2 行,如下所示。 *我的帖子解释了一个触发器(带有视图):
CREATE TABLE person (
id INTEGER,
name VARCHAR(20)
);
INSERT INTO person (id, name)
VALUES (1, 'John'), (2, 'David');
接下来,创建
log
表,然后将num
为0
的行插入到log
表中,如下所示:
CREATE TABLE log (
num INTEGER
);
INSERT INTO log (num) VALUES (0);
现在,您可以使用
my_func()
和 RETURNS trigger
创建 LANGUAGE plpgsql
触发函数,使 num
递增 1,如下所示:
CREATE FUNCTION my_func() RETURNS trigger
AS $$
BEGIN
UPDATE log SET num = num + 1;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
接下来,您创建
my_v
视图,如下所示。 *我的帖子详细解释了一个视图:
CREATE VIEW my_v AS
SELECT * FROM person;
现在,您可以创建
my_t
触发器,当每行的 my_func()
上阻止 UPDATE
或 DELETE
操作时,运行 my_view
,如下所示:
CREATE TRIGGER my_t INSTEAD OF UPDATE OR DELETE ON my_v
FOR EACH ROW EXECUTE FUNCTION my_func();
*备注:
INSTEAD OF
可以阻止操作。
您可以将
INSTEAD OF
与视图和 FOR EACH ROW
一起使用,但不能与函数和 FOR EACH STATEMENT
一起使用,否则会出现错误。
使用
UPDATE OF
时不能用INSTEAD OF
指定一列或多列,否则会出错。
视图上带有
BEFORE
或 AFTER
以及 INSERT
、UPDATE
或 DELETE
的触发器不起作用。 *我的问题和答案详细解释了。
然后,用
person
向 my_v
表插入一行,则 num
仍然是 0
,如下所示:
postgres=# INSERT INTO my_v (id, name) VALUES (3, 'Robert');
INSERT 0 1
postgres=# SELECT * FROM person;
id | name
----+--------
1 | John
2 | David
3 | Robert
(3 rows)
postgres=# SELECT num FROM log;
num
-----
0
(1 row)
然后,您尝试使用
name
将 Tom
表上所有 3 行的 person
更新为 my_v
,然后更新被阻止,然后 num
变为 3
,如下所示:
postgres=# UPDATE my_v SET name = 'Tom';
UPDATE 0
postgres=# SELECT * FROM person;
id | name
----+--------
1 | John
2 | David
3 | Robert
(3 rows)
postgres=# SELECT num FROM log;
num
-----
3
(1 row)
然后,您尝试从
person
表中删除 2 行,其中 id
为 2
,3
为 my_v
,则删除被阻止,然后 num
为 5
,如下所示:
postgres=# DELETE FROM my_v WHERE id IN (2, 3);
DELETE 0
postgres=# SELECT * FROM person;
id | name
----+--------
1 | John
2 | David
3 | Robert
(3 rows)
postgres=# SELECT num FROM log;
num
-----
5
(1 row)