postgresql-触发器

news2024/12/23 22:13:27

postgresql-触发器

  • 触发器概述
  • 创建触发器
  • 管理触发器
  • 删除触发器
  • 事件触发器
    • 创建事件触发器
    • 修改触发器
    • 删除事件触发器

触发器概述

PostgreSQL 触发器(trigger)是一种特殊的函数,当某个数据变更事件(INSERT、UPDATE、
DELETE 或者 TRUNCATE 语句)或者数据库事件(DDL 语句)发生时自动执行,而不是由用
户或者应用程序进行调用

基于某个表或者视图数据变更的触发器被称为数据变更触发器(DML 触发器),基于数据
库事件的触发器被称为事件触发器(DDL 触发器)。一般我们更多使用的是数据变更触发器
在这里插入图片描述
对于数据变更触发器,PostgreSQL 支持两种级别的触发方式:行级(row-level)触发器和 语句级(statement-level)触发器。这两者的区别在于触发的时机和触发次数。例如,对于一个
影响 20 行数据的 UPDATE 语句,行级触发器将会触发器 20 次,而语句级触发器只会触发 1 次

触发器可以在事件发生之前(BEFORE)或者之后(AFTER)触发。如果在事件之前触发,
它可以跳过针对当前行的修改,甚至修改被更新或插入的数据;如果在事件之后触发,触发器可
以获得所有的变更结果。INSTEAD OF 触发器可以用于替换数据变更的操作,但只能基于视图
定义

下表列出了 PostgreSQL 中支持的各种触发器:

触发时机触发事件行级触发器语句级触发器
beforeinsert update delete表和外部表表、视图和外部表
beforetruncate
afterinsert update delete表和外部表表 视图和外部表
aftertruncate
instead ofinsert update delete视图

触发器对于多应用共享的数据库而言非常有用,可以将跨应用的功能存储在数据库中,当表
中的数据发生任何变化时都会自动执行触发器的操作。例如,可以用触发器实现数据修改的历史
审计,而不需要各种应用程序实现任何相关的逻辑。

另外,触发器还可以用于实现复杂的数据完整性和业务规则。例如,在非业务时间不允许修
改用户的信息。

但是另一方面,触发器可能带来的问题就是在不清楚它们的存在和逻辑时可能会影响数据修
改的结果和性能。

创建触发器

PostgreSQL 触发器的创建分为两步:

  1. 使用 CREATE FUNCTION 语句创建一个触发器函数
  2. 使用 CREATE TRIGGER 语句将该函数与表进行关联
-- 语法
CREATE [ OR REPLACE ] FUNCTION trigger_function ()
 RETURNS trigger
AS $$
DECLARE
 declarations
BEGIN
 statements;
 ...
END; $$
LANGUAGE plpgsql;

触发器函数与普通函数的区别在于它没有参数,并且返回类型为 trigger;在触发器函数的内部,系统自动创建了许多特殊的变量:

  • NEW ,类型为 RECORD,代表了行级触发器 INSERT、UPDATE 操作之后的新数据行。
    对于 DELETE 操作或者语句级触发器而言,该变量为 null;
  • OLD,类型为 RECORD,代表了行级触发器 UPDATE、DELETE 操作之前的旧数据行。
    对于 INSERT 操作或者语句级触发器而言,该变量为 null;
  • TG_NAME,触发器的名称;
  • TG_WHEN,触发的时机,例如 BEFORE、AFTER 或者 INSTEAD OF
  • TG_LEVEL,触发器的级别,ROW 或者 STATEMENT;
  • TG_OP,触发的操作,INSERT、UPDATE、DELETE 或者 TRUNCATE;
  • TG_RELID,触发器所在表的 oid;
  • TG_TABLE_NAME,触发器所在表的名称;
  • TG_TABLE_SCHEMA,触发器所在表的模式;
  • TG_NARGS,创建触发器时传递给触发器函数的参数个数;
  • TG_ARGV[],创建触发器时传递给触发器函数的具体参数,下标从 0 开始。非法的下标
    (小于 0 或者大于等于 tg_nargs)将会返回空值。
-- 使用 CREATE TRIGGER 语句创建一个触发器,语法如下:
CREATE TRIGGER trigger_name
{BEFORE | AFTER | INSTEAD OF} {event [OR ...]}
 ON table_name
 [FOR [EACH] {ROW | STATEMENT}]
 [WHEN ( condition ) ]
 EXECUTE FUNCTION trigger_function;

其中,event 可以是 INSERT、UPDATE、DELETE 或者 TRUNCATE,UPDATE 支持特定字
段(UPDATE OF col1, clo2)的更新操作;触发器可以在事件之前(BEFORE)或者之后(AFTER)
触发,INSTEAD OF 只能用于替代视图上的 INSERT、UPDATE 或者 DELETE 操作;FOR EACH
ROW 表示行级触发器,FOR EACH STATEMENT 表示语句级触发器;WHEN 用于指定一个额
外的触发条件,满足条件才会真正支持触发器函数

我们通过触发器来实现记录员工的信息变更历史,首先创建一个历史记录表
employees_history:

create table employees_history (
 id serial primary key,
 employee_id int null,
 first_name varchar(20) null,
 last_name varchar(25) null,
 email varchar(25) null,
 phone_number varchar(20) null,
 hire_date date null,
 job_id varchar(10) null,
 salary numeric(8,2) null,
 commission_pct numeric(2,2) null,
 manager_id int null,
 department_id int null,
 action_type varchar(10) not null,
 change_dt timestamp not null
);
-- 定义一个触发器函数 track_employees_change
-- 该函数根据不同的操作记录了相应的历史信息、操作类型和操作时间。
create or replace function track_emp_change()
returns trigger 
as $$
begin 
	-- tg_op 触发的操作 
	if tg_op = 'INSERT' then
		insert into public.employees_history
		(employee_id, first_name, last_name, email, phone_number, 
		hire_date, job_id, salary, commission_pct, manager_id,
		department_id, action_type, change_dt)
		values(new.employee_id, new.first_name, new.last_name, 
		new.email, new.phone_number, new.hire_date, new.job_id, new.salary,
		new.commission_pct, new.manager_id, new.department_id,'INSERT'
		,current_timestamp);
	elsif tg_op = 'UPDATE' then
		insert into public.employees_history
		(employee_id, first_name, last_name, email, phone_number, 
		hire_date, job_id, salary, commission_pct, manager_id,
		department_id, action_type, change_dt)
		values(old.employee_id, old.first_name, old.last_name, 
		old.email, old.phone_number, old.hire_date, old.job_id, old.salary,
		old.commission_pct, old.manager_id, old.department_id,'UPDATE'
		,current_timestamp);
	elsif tg_op = 'DELETE' then
		insert into public.employees_history
		(employee_id, first_name, last_name, email, phone_number, 
		hire_date, job_id, salary, commission_pct, manager_id,
		department_id, action_type, change_dt)
		values(old.employee_id, old.first_name, old.last_name, 
		old.email, old.phone_number, old.hire_date, old.job_id, old.salary,
		old.commission_pct, old.manager_id, old.department_id, 'DELETE'
		,current_timestamp);
	end if;
	return new;
end $$
language plpgsql;
-- 最后创建一个触发器 trg_employees_change,将该函数与 employees 进行关联:
create trigger trg_employees_change
 before insert or UPDATE or DELETE
 on employees
 for each row
 execute function public.track_emp_change();
-- 测试
insert into employees(employee_id, first_name, last_name, email, phone_number,
hire_date, job_id, salary, commission_pct, manager_id, department_id)
values(208, 'Tony', 'Dong', 'TonyDong', '01066665678', '2020-05-25',
'IT_PROG', 6000, null, 103, 60);
SELECT * FROM employees_history;
--我们往 employees 中插入一条记录之后,employees_history 记录了这一操作历史;对于
--UPDATE 和 DELETE 操作也是如此

在这里插入图片描述

管理触发器

PostgreSQL 提供了 ALTER TRIGGER 语句,用于修改触发器:

-- 修改触发器的名称
ALTER TRIGGER name ON table_name RENAME TO new_name;
-- PostgreSQL 还支持触发器的禁用和启用:
ALTER TABLE table_name
{ENABLE | DISABLE} TRIGGER {trigger_name | ALL | USER};

默认创建的触发器处于启用状态;我们也可以使用以上语句禁用或者启用某个触发器、某个
表上的所有触发器(ALL)或用户触发器(不包括内部生成的约束触发器,例如用于外键约束或
延迟唯一性约束以及排除约束的触发器)

视图 information_schema.triggers 中存储了关于触发器的信息

select * from information_schema.triggers;

在这里插入图片描述

删除触发器

-- 被禁用的触发器仍然存在,只是不会被触发;如果想要删除触发器,可以使用 DROP TRIGGER 语句
DROP TRIGGER [IF EXISTS] trigger_name
ON table_name [RESTRICT | CASCADE];

IF EXISTS 可以避免触发器不存在时的错误提示;CASCADE 表示级联删除依赖于该触发器
的对象,RESTRICT 表示如果存在依赖于该触发器的对象返回错误,默认为 RESTRICT

-- 将 employees 表上的触发器 trg_employees_change 删除
-- 虽然删除了触发器,但是触发器函数 track_employees_change 仍然存在。
drop trigger trg_employees_change on employees;

在这里插入图片描述

事件触发器

除了数据变更触发器之外,PostgreSQL 还提供了另一种触发器:事件触发器 。事件触发器
主要用于捕获全局的 DDL 事件,目前支持 ddl_command_start、ddl_command_end、table_rewrite
和 sql_drop,这些事件支持的完整语句可以参考官网

对于事件触发器的函数而言,同样预定义了两个变量:

  • TG_EVENT,触发事件;
  • TG_TAG,触发语句。
    对于事件触发器,首先也需要创建一个函数,返回类型为 event_trigger
create or replace function abort_any_command()
returns event_trigger
as $$
begin 
-- 判断当前操作用户是否为超级用户(postgres),如果不是则不允许执行任何 DDL语句。
	if (user != 'postgres') then
	 raise exception '禁止%命令',tg_tag;	
	end if;
end $$
language plpgsql;

创建事件触发器

-- 使用 create event trigger 语句创建事件触发器:
create event trigger abort_ddl on ddl_command_start
 execute function abort_any_command();

修改触发器

-- alter event trigger 语句可以启用/禁用事件触发器或者修改触发器的名称等:
alter event trigger name disable;
alter event trigger name enable;
alter event trigger name rename to new_name;

删除事件触发器

--drop event trigger 语句可以用于删除事件触发器:
drop event trigger [ if exists ] name [ cascade | restrict ];
-- 删除事件触发器 abort_ddl 
drop event trigger abort_ddl;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1035047.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

6 年大厂程序员跟你聊聊,算法其实没那么难,要怎么准备比较好

说起算法,许多程序员都会一顿哀嚎,为啥面试要靠算法这个东西。不过这个不是咱们讨论的重点。(我们无法改变这种现状,那就改变自己) 今天,我们一起来聊一下,程序员面试的时候该如何准备算法。 …

数字图像基础,数字图像处理中的基础内容(数字图像处理概念 P2)

文章目录 人类视觉系统构造数字图像生成采样和量化像素之间的基本关系 人类视觉系统构造 锥状体:明亮的视野杆状体:微光或暗视野图像成像原理:类似照相机亮度适应现象:人的视觉不能同时在一个范围内工作同时对比现象:…

怒刷LeetCode的第14天(Java版)

目录 第一题 题目来源 题目内容 解决方法 方法一:动态规划 方法二:栈 方法三:双指针 第二题 题目来源 题目内容 解决方法 方法一:二分查找 方法二:线性扫描 方法三:递归 第三题 题目来源 …

【WSL】仅适用于装C盘情况-用WSL在win10安装LInux

研究了一点点伪分布式的内容。决定搞一个Linux系统玩一下 参考来自微软官方安装步骤: 旧版 WSL 的手动安装步骤 https://learn.microsoft.com/zh-cn/windows/wsl/install-manual WSL全称为,Windows Subsystem for Linux 法一:应用商店装 查…

你写过的最蠢的代码是?

🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页: 🐅🐾猫头虎的博客🎐《面试题大全专栏》 🦕 文章图文并茂&#x1f996…

RestTemplate:简化HTTP请求的强大工具

文章目录 什么是RestTemplateRestTemplate的作用代码示例 RestTemplate与HttpClient 什么是RestTemplate RestTemplate是一个在Java应用程序中发送RESTful HTTP请求的强大工具。本文将介绍RestTemplate的定义、作用以及与HttpClient的对比,以帮助读者更好地理解和使…

资料分析笔记

统计术语 现期:现在的时间 基期:之前的时间 现期量 基期量 增长量(有正负) 增长率 【增幅、增速、r】(有正负) 同比:例:2014年5月 和 2013年5月 环比:例:20…

Vue通过配置代理解决跨域问题

VueCli2.0版本 1.找到config文件目录下的dev.env.js以及prod.env.js文件 dev.env.js是配置本地环境的 prod.env.js是配置正式环境的 分别在这两个文件下的API_URL中写下对应的正式服测试服域名 下面的是正式服的域名 2.配置跨域 //vue2.0 //config -> index.js proxyt…

[C++ 网络协议] 多线程服务器端

具有代表性的并发服务器端实现模型和方法: 多进程服务器:通过创建多个进程提供服务。 多路复用服务器:通过捆绑并统一管理I/O对象提供服务。 多线程服务器:通过生成与客户端等量的线程提供服务。✔ 目录 1. 线程的概念 1.1 为什…

【MySQL】 MySQL索引事务

文章目录 🛫索引🎍索引的概念🌳索引的作用🎄索引的使用场景🍀索引的使用📌查看索引📌创建索引🌲删除索引 🌴索引保存的数据结构🎈B树🎈B树&#x…

【python】Seaborn画热力图,只显示第一行数字---seaborn与matplotlib版本问题

github上有这个讨论:Heatmap only has annotation text in the top row only Issue #3478 mwaskom/seaborn (github.com)翻译过来就是:热图仅在最上面一行有注释文本; 原因就是matplotlib 在2023年9月更新到了 3.8.0版本,改变了…

pyspark常用算子总结

欢迎关注微信公众号,更多优质内容会在微信公众号首发 1. pyspark中时间格式的数据转换为字符串格式的时间,示例代码 from datetime import datetimedate_obj datetime(2023, 7, 2) formatted_date date_obj.strftime("%Y-%m-%d %H:%M:%S")p…

Spring循环依赖大全

本博客挑出出现大部分情况的循环依赖场景进行分析,分析启动会不会报循环依赖的错误! 一、常规的A依赖B,B依赖A,代码如下: Component public class A {Resourceprivate B b; } Component public class B {Resourcepri…

Codeforces Round 898 (Div. 4)

Dashboard - Codeforces Round 898 (Div. 4) - Codeforces F. Money Trees 双指针&#xff08;需要细心&#xff09; #include<bits/stdc.h> using namespace std; const int N 2e5 10; void solve() {int n, k, ans 0, a[N], h[N];cin >> n >> k;for(…

Java基础(五)

前言&#xff1a;本博客主要记录学习网络通信的基本知识以及网络编程的应用。 网络编程 基本的通信架构 CS架构&#xff08;Cilent客户端/Server服务端&#xff09; BS架构&#xff08;Browser浏览器/Server服务端&#xff09; 网络通信三要素 网络通信三要素&#xff1a;…

【02】FISCOBCOS搭建区块链网络

官方文档https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/docs/installation.html 第一步. 安装依赖 开发部署工具 build_chain.sh脚本依赖于openssl, curl&#xff0c;根据您使用的操作系统&#xff0c;使用以下命令安装依赖。 sudo apt install -y openssl …

【深度学习实验】前馈神经网络(九):整合训练、评估、预测过程(Runner)

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. __init__(初始化) 2. train(训练) 3. evaluate(评估) 4. predict(预测) 5. save_model 6. load_model 7. 代码整合 一、实验介绍 二、实验环境 本系列实验使用…

latexocr安装过程中遇到的问题解决办法

环境要求&#xff1a;需要Python版本3.7&#xff0c;并安装相应依赖文件 具体的详细安装步骤可见我上次写的博文&#xff1a;Mathpix替代者|科研人必备公式识别插件|latexocr安装教程 ‘latexocr‘ 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件的相关解决办…

获取文件创建时间

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl Java源码 public void testGetFileTime() {try {String string "E://test.txt";File file new File(string);Path path file.toPath();BasicFileAttributes ba…

Python 运行代码

一、Python运行代码 可以使用三种方式运行Python&#xff0c;如下&#xff1a; 1、交互式 通过命令行窗口进入 Python 并开始在交互式解释器中开始编写 Python 代码 2、命令行脚本 可以把代码放到文件中&#xff0c;通过python 文件名.py命令执行代码&#xff0c;如下&#xff…