
PostgreSQL中的触发器
BEFORE触发器:在操作发生前触发。 AFTER触发器:在操作发生后触发。 INSTEAD OF触发器:在对视图执行操作时触发,用于替代原操作。
发布日期:2021-05-08 12:23:11
浏览次数:10
分类:精选文章
本文共 5459 字,大约阅读时间需要 18 分钟。
PostgreSQL 触发器使用指南
触发器概述
触发器是PostgreSQL中一个强大的功能,它允许在特定数据库操作发生时自动执行预定义的函数。这些触发器可以附加到表、视图或外表上,并根据操作类型(INSERT、UPDATE、DELETE)触发相应的函数。
触发器的类型
触发器可以根据触发时间分为以下几种类型:
触发器的定义
在创建触发器之前,需要先定义一个触发器函数。该函数必须满足以下条件:
- 必须是无参函数。
- 必须返回
trigger
类型。
如何创建触发器
在定义好触发器函数后,可以使用CREATE TRIGGER
命令来创建触发器。一个触发器函数可以用于多个触发器。
触发器的执行顺序
如果同一对象上定义了多个触发器,触发器将按照字母顺序执行。
触发器的可见性
触发器函数的可见性遵循以下规则:
- 语句级BEFORE触发器在操作发生前执行,且对触发器函数不可见。
- 行级触发器的可见性取决于触发时间:
- 行级BEFORE触发器在操作前执行,对触发器函数不可见。
- 行级AFTER触发器在操作后执行,对触发器函数可见。
- 行级INSTEAD OF触发器在操作中立即触发,且对触发器函数可见。
数据变更的可见性
在触发器函数中执行SQL命令时,需要注意数据变更的可见性规则:
- 语句级触发器遵循简单可见性规则:语句级BEFORE触发器对触发器函数不可见,而语句级AFTER触发器可见。
- 行级触发器的可见性取决于触发时间:
- 行级BEFORE触发器在操作前执行,对触发器函数不可见。
- 行级AFTER触发器在操作后执行,对触发器函数可见。
- 行级INSTEAD OF触发器在操作中立即触发,且对触发器函数可见。
使用C编写触发器函数
如果你选择使用C语言编写触发器函数,可以按照以下步骤进行:
包含必要的头文件:
#include "postgres.h"#include "fmgr.h"#include "executor/spi.h"#include "commands/trigger.h"#include "utils/rel.h"PG_MODULE_MAGIC;PG_FUNCTION_INFO_V1(trigf);
编写触发器函数:
Datum trigf(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *)fcinfo->context; char *when; bool checknull = false; if (!CALLED_AS_TRIGGER(fcinfo)) { elog(ERROR, "trigf: not called by trigger manager"); } when = TRIGGER_FIRED_BEFORE(trigdata->tg_event) ? "before" : "after "; TupDesc tupdesc = trigdata->tg_relation->rd_att; if ((ret = SPI_connect()) < 0) { elog(ERROR, "trigf (fired %s): SPI_connect returned %d", when, ret); } if (TRIGGER_FIRED_BEFORE(trigdata->tg_event)) { rettuple = trigdata->tg_trigtuple; } else { rettuple = trigdata->tg_newtuple; } if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event) && TRIGGER_FIRED_BEFORE(trigdata->tg_event)) { checknull = true; } if (TRIGGER_FIRED_BEFORE(trigdata->tg_event)) { when = "before"; } else { when = "after "; } if ((ret = SPI_exec("SELECT count(*) FROM ttest", 0)) < 0) { elog(ERROR, "trigf (fired %s): SPI_exec returned %d", when, ret); } i = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[0], tupdesc, 1, &isnull)); elog(INFO, "trigf (fired %s): there are %d rows in ttest", when, i); SPI_finish(); if (checknull) { if (isnull) { rettuple = NULL; } } return PointerGetDatum(rettuple);}
创建触发器函数和触发器:
CREATE FUNCTION trigf() RETURNS trigger AS 'filename' LANGUAGE C;CREATE TRIGGER tbefore BEFORE INSERT OR UPDATE OR DELETE ON ttest FOR EACH ROW EXECUTE FUNCTION trigf();CREATE TRIGGER tafter AFTER INSERT OR UPDATE OR DELETE ON ttest FOR EACH ROW EXECUTE FUNCTION trigf();
触发器的完整示例
以下是一个简单的C触发器函数示例:
创建表
CREATE TABLE ttest (x integer);
触发器函数源码
#include "postgres.h"#include "fmgr.h"#include "executor/spi.h"#include "commands/trigger.h"#include "utils/rel.h"PG_MODULE_MAGIC;PG_FUNCTION_INFO_V1(trigf);Datum trigf(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *)fcinfo->context; TupleDesc tupdesc; HeapTuple rettuple; char *when; bool checknull = false; bool isnull; int ret, i; if (!CALLED_AS_TRIGGER(fcinfo)) { elog(ERROR, "trigf: not called by trigger manager"); } when = TRIGGER_FIRED_BEFORE(trigdata->tg_event) ? "before" : "after "; tupdesc = trigdata->tg_relation->rd_att; if ((ret = SPI_connect()) < 0) { elog(ERROR, "trigf (fired %s): SPI_connect returned %d", when, ret); } if (TRIGGER_FIRED_BEFORE(trigdata->tg_event)) { rettuple = trigdata->tg_trigtuple; } else { rettuple = trigdata->tg_newtuple; } if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event) && TRIGGER_FIRED_BEFORE(trigdata->tg_event)) { checknull = true; } if (TRIGGER_FIRED_BEFORE(trigdata->tg_event)) { when = "before"; } else { when = "after "; } if ((ret = SPI_exec("SELECT count(*) FROM ttest", 0)) < 0) { elog(ERROR, "trigf (fired %s): SPI_exec returned %d", when, ret); } i = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[0], tupdesc, 1, &isnull)); elog(INFO, "trigf (fired %s): there are %d rows in ttest", when, i); SPI_finish(); if (checknull) { if (isnull) { rettuple = NULL; } } return PointerGetDatum(rettuple);}
创建触发器
CREATE FUNCTION trigf() RETURNS trigger AS 'filename' LANGUAGE C;CREATE TRIGGER tbefore BEFORE INSERT OR UPDATE OR DELETE ON ttest FOR EACH ROW EXECUTE FUNCTION trigf();CREATE TRIGGER tafter AFTER INSERT OR UPDATE OR DELETE ON ttest FOR EACH ROW EXECUTE FUNCTION trigf();
测试触发器
-- INSERT INTO ttest VALUES (NULL);INFO: trigf (fired before): there are 0 rows in ttestINSERT 0 0-- Insertion skipped and AFTER trigger is not fired-- INSERT INTO ttest VALUES (1);INFO: trigf (fired before): there are 0 rows in ttestINFO: trigf (fired after): there are 1 rows in ttest-- INSERT INTO ttest SELECT x * 2 FROM ttest;INFO: trigf (fired before): there are 1 rows in ttestINFO: trigf (fired after): there are 2 rows in ttest-- UPDATE ttest SET x = NULL WHERE x = 2;INFO: trigf (fired before): there are 2 rows in ttestUPDATE 0-- UPDATE ttest SET x = 4 WHERE x = 2;INFO: trigf (fired before): there are 2 rows in ttestINFO: trigf (fired after): there are 2 rows in ttestUPDATE 1vac-- DELETE FROM ttest;INFO: trigf (fired before): there are 2 rows in ttestINFO: trigf (fired before): there are 1 rows in ttestINFO: trigf (fired after): there are 0 rows in ttestINFO: trigf (fired after): there are 0 rows in ttest-- SELECT * FROM ttest;(0 rows)
更多示例和详细文档,请参考PostgreSQL官方文档和相关开发资源。
发表评论
最新留言
哈哈,博客排版真的漂亮呢~
[***.90.31.176]2025年03月24日 17时02分05秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
BottomNavigationView控件item多于3个时文字不显示
2019-03-05
函数指针的典型应用-计算函数的定积分(矩形法思想)
2019-03-05
8051单片机(STC89C52)八个LED灯闪烁
2019-03-05
8051单片机(STC89C52)以定时器中断模式实现两倒计时器异步计时
2019-03-05
用 wxPython 打印你的 App
2019-03-05
vue项目通过vue.config.js配置文件进行proxy反向代理跨域
2019-03-05
Linux下安装MySql过程
2019-03-05
android:使用audiotrack 类播放wav文件
2019-03-05
vue通过better-scroll 封装自定义的下拉刷新组件
2019-03-05
android解决:使用多线程和Handler同步更新UI
2019-03-05
vue自定义封装Loading组件
2019-03-05
解决移动端项目中苹果ios和安卓android手机点击输入框网页页面自动放大缩小
2019-03-05
Element UI 中动态路由的分析及实现
2019-03-05
使用springMVC配置视图管理器后找不到指定的页面
2019-03-05
关于js中对于Promise的深入理解
2019-03-05
杭电 2007 平方和与立方和(输入数据的大小顺序并不能默认)
2019-03-05
十大排序算法之三:插入排序(Python)
2019-03-05
利用Python实现循环队列
2019-03-05
利用递归实现二叉树的前中后序遍历(Python)
2019-03-05
Python刷题输入输出
2019-03-05