instead of 触发器
专栏内容:
文章目录
概述
在postgresql 中,触发器与SQL标准相比,有一个扩展特性就是可以在视图上创建触发器。
在视图上的触发器类型是’INSTEAD OF`,可以在insert, update, delete事件的before或after进行触发,
本文就来详细分享一下,并通过一个实用的审计数据的案例来展示效果。
机制原理
下面先来介绍SQL语法,然后通过介绍它的执行过程,了解它的机制原理。
INSTEAD OF 触发器语法
视图触发器的创建语法如下:
CREATE TRIGGER trigger_name
INSTEAD OF [INSERT OR UPDATE OR DELETE]
ON table_name
FOR EACH ROW
EXECUTE FUNCTION trigger_function;
在视图上的触发器,类型只有INSTEAT OF,而且它对应的触发器件有insert,update,delete,此处没有truncate事件。
另外,instead of 触发器也只能定义为行级触发器,这个原因下面会分析。
INSTEAD OF 触发器执行步骤
视图触发器执行的步骤如下
- 当在视图上执行INSERT、UPDATE或DELETE操作时,如果为该视图定义了INSTEAD OF触发器,那么这些操作不会直接对视图或其基础表执行。
- 相反地,INSTEAD OF触发器会代替这些操作执行。也就是说,对于需要修改的每一行,都会触发INSTEAD OF触发器。
- 触发器的函数需要负责执行必要的修改到视图的基础表,并在适当的情况下返回修改后的行,以便在视图中显示。
- 这允许开发者在不影响基础表结构的情况下,通过视图执行复杂的操作逻辑。
如果视图上没有INSTEAD OF触发器,那么对视图的操作必须被转换为对基础表的操作,是直接操作基础表。
多触发器的优先级
- 除了INSTEAD OF触发器外,还可以在视图上定义在INSERT、UPDATE或DELETE操作之前或之后执行的触发器。
- 但是,这些触发器只有在视图上也有INSTEAD OF触发器的情况下才会被触发。
- 如果没有INSTEAD OF触发器,那么任何针对视图的SQL语句都必须被重写为影响基础表的语句,然后触发的是附加到基础表上的触发器。
审计案例解析
为了信息的安全,一般系统都会有审计这个功能,其中审计日志会把操作记录详细记录下来,会定期时行审计或者出问题时能够帮助回溯。
下面就来分享一个通过触发器实现的审计日志功能,大概设计如下:
- 应用一般不会对物理表进行直接操作,而对给它们创建对应的视图,只看到部分数据;
- 对于每个视图的操作事件,创建instead of类型的触发器;
- 在视图触发器中,对于操作的类型,数据,以及操作者用户都记录到表中;
数据准备
创建两张数据表
- 一张是数据信息表emp,记录员工的薪记信息;
- 一张是审计数据表emp_audit, 记录操作类型,操作员,数据变动,以及时间。
CREATE TABLE emp (
empname text PRIMARY KEY,
salary integer
);
CREATE TABLE emp_audit(
operation char(1) NOT NULL,
userid text NOT NULL,
empname text NOT NULL,
salary integer,
stamp timestamp NOT NULL
);
创建视图
CREATE VIEW emp_view AS
SELECT e.empname,
e.salary,
max(ea.stamp) AS last_updated
FROM emp e
LEFT JOIN emp_audit ea ON ea.empname = e.empname
GROUP BY 1, 2;
创建执行函数
- 在delete时,先删除基础表的数据;如果删除成功,再插入审计表,操作类型为’D’,否则不操作审计表;
- 在update时,同上,先更新基础表;如果成功,才插入审计表;
- 在insert时,先插入基础表;然后操作审计表;
- 每次都将操作时间更新为当前时间;
CREATE OR REPLACE FUNCTION update_emp_view() RETURNS TRIGGER AS $$
BEGIN
IF (TG_OP = 'DELETE') THEN
DELETE FROM emp WHERE empname = OLD.empname;
IF NOT FOUND THEN
RETURN NULL;
END IF;
OLD.last_updated = now();
INSERT INTO emp_audit VALUES('D', current_user, OLD.*);
RETURN OLD;
ELSIF (TG_OP = 'UPDATE') THEN
UPDATE emp SET salary = NEW.salary WHERE empname = OLD.empname;
IF NOT FOUND THEN
RETURN NULL;
END IF;
NEW.last_updated = now();
INSERT INTO emp_audit VALUES('U', current_user, NEW.*);
RETURN NEW;
ELSIF (TG_OP = 'INSERT') THEN
INSERT INTO emp VALUES(NEW.empname, NEW.salary);
NEW.last_updated = now();
INSERT INTO emp_audit VALUES('I', current_user, NEW.*);
RETURN NEW;
END IF;
END;
$$ LANGUAGE plpgsql;
创建触发器
在视图emp_view上创建instead of类型的触发器,使用上面定义的函数。
CREATE TRIGGER emp_audit
INSTEAD OF INSERT OR UPDATE OR DELETE ON emp_view
FOR EACH ROW EXECUTE FUNCTION update_emp_view();
结果展示
测试一下审计模块的效果。
信息查看
员工管理系统中,对于薪资表结构如下:
postgres=> \d emp_view
View "senlleng.emp_view"
Column | Type | Collation | Nullable | Default
--------------+-----------------------------+-----------+----------+---------
empname | text | | |
salary | integer | | |
last_updated | timestamp without time zone | | |
Triggers:
emp_audit INSTEAD OF INSERT OR DELETE OR UPDATE ON emp_view FOR EACH ROW EXECUTE FUNCTION update_emp_view()
新员工入职
有新员工入职,录入新员工的薪资。
postgres=> insert into emp_view values('zhanglei', 10000);
INSERT 0 1
postgres=> insert into emp_view values('wangguo', 8000);
INSERT 0 1
postgres=> select * from emp_audit ;
operation | userid | empname | salary | stamp
-----------+----------+----------+--------+----------------------------
I | senllang | zhanglei | 10000 | 2024-06-06 08:13:05.829596
I | senllang | wangguo | 8000 | 2024-06-06 08:13:24.125127
(2 rows)
有两名新员工入职,可以看到是那个操作员录入的,并且当前录入的时间,信息都可以看到。
变岗调薪
当人员调岗时,对应的薪资也会发生变化;
或者人员离职时,需要删除对应的记录。
postgres=> update emp_view set salary = 7500 where empname='wangguo';
UPDATE 1
postgres=> delete from emp_view where empname='zhanglei';
DELETE 1
postgres=> select * from emp_audit ;
operation | userid | empname | salary | stamp
-----------+----------+----------+--------+----------------------------
I | senllang | zhanglei | 10000 | 2024-06-06 08:13:05.829596
I | senllang | wangguo | 8000 | 2024-06-06 08:13:24.125127
U | senllang | wangguo | 7500 | 2024-06-06 08:14:30.737416
D | senllang | zhanglei | 10000 | 2024-06-06 08:14:53.089083
(4 rows)
当然审计表的权限是非常高的,只有在审计系统中才能查看,而且审计数据是不能删除的。
总结
在视图上的instead of 触发器,可以将原本的执行计划重写 替换为触发器执行,这样可以进行更为复杂的动作,这里以审计为例,演示了触发器的效果。
结尾
非常感谢大家的支持,在浏览的同时别忘了留下您宝贵的评论,如果觉得值得鼓励,请点赞,收藏,我会更加努力!
作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。
注:未经同意,不得转载!