关于postgresql自定义函数和触发器的一些问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/BeauXie/article/details/77586652

一、前言

最近接触公司的一个项目,需要做一个数据同步模块,所以要用到触发器。但坑爹的是用的是postgresql数据库而非MySQL,不得不说遇到了很多的坑。本文中的内容基本上都是自己网上查找资料,参考别人的博客,再加自己摸索,才实现的,且已经用到了项目中。可能不是最优的,仅供参考,如果有更好的方法,请留言告知,谢谢!

需求说明:dms_sync是存放数据同步表,如果其他需要同步的表中记录有增删改操作,都会通过触发器往dms_sync表中增加一条数据,如dms_sync表中有以下一条记录:


这说明,用户在2017-09-02 15:39:31往dms_dept表中新插入了一条id为b22ea7c304494dfe8c5cb339930da806的记录。

postgresql的触发器需要用函数来执行,所以要实现以上的效果,还得先自定义函数。

二、自定义函数

1.创建函数模板

CREATE [OR REPLACE] FUNCTION function_name (arguments) 
RETURNS return_datatype AS $variable_name$
  DECLARE
    declaration;
    [...]
  BEGIN
    < function_body >
    [...]
    RETURN { variable_name | value }
  END; LANGUAGE plpgsql; 

看模板不如直接看例子。。。

2.创建普通函数

(1)创建自定义uuid()函数

postgresql默认是没有像mysql那样有uuid()函数的,虽然可以拓展,但考虑到项目是给用户用的。如果让用户去装拓展、或者每次初始化数据库时都要安装拓展,有点麻烦。如果自己能创建一个函数用来产生uuid,这样把函数放入初始化数据库脚本即可。函数如下:

-------------------
--创建UUID函数
------------------
CREATE OR REPLACE FUNCTION uuid () RETURNS TEXT AS $BODY$
DECLARE passed BOOLEAN ;
BEGIN
	RETURN (
		SELECT
			REPLACE (
				uuid_in (
					md5(
					random() :: TEXT || now() :: TEXT
					) :: cstring
				) :: TEXT,
				'-' :: TEXT,
				'' :: TEXT
			)
	) ; RETURN passed ;
END ; $BODY$ LANGUAGE 'plpgsql' VOLATILE COST 100;
说明一下:

 加上 OR REPLACE的好处在于, 如果函数存在则替代,即允许修改一个存在的函数.

运行结果:

(2)创建带参的insert_sync(recid TEXT,tbname TEXT,typ TEXT)函数

这个函数的作用是触发器触发时,往dms_sync表中插入数据。如下:

------------------------
--创建insert_sync(recid TEXT,tbname TEXT,typ TEXT)函数
------------------------
DROP FUNCTION IF EXISTS insert_sync(recid TEXT,tbname TEXT,typ TEXT);
CREATE OR REPLACE FUNCTION insert_sync (
	recid TEXT,
	tbname TEXT,
	typ TEXT
) RETURNS BOOLEAN AS $$
DECLARE passed BOOLEAN ;
BEGIN
	INSERT INTO dms_sync (
		ID,
		recid,
		tbname,
		typ,
		createtime
	)
VALUES
	(uuid(), $1, $2, $3, to_char(now(), 'yyyy-mm-dd HH24:MI:ss')) ; RETURN passed ;
END ; $$ LANGUAGE plpgsql VOLATILE COST 100;
说明一下:

其中的uuid()是自定义的函数,$1表示第一个参数,$2表示第二个函数,一次类推...postgresql函数的参数使用不支持通过参数名,只能通过$1、$2这样来调用。

3.创建触发器函数

触发器函数是触发器触发时调用,函数返回的类型必须是TRIGGER ,且不能有任何参数。在这里建一个trigger_insert_sync()触发器函数,如下:

------------------------
--创建trigger_insert_sync函数
------------------------
--触发器函数可直接覆盖插入,因此无须判断
CREATE OR REPLACE FUNCTION trigger_insert_sync () RETURNS TRIGGER AS $$
DECLARE passed BOOLEAN ;
BEGIN

IF (TG_OP = 'DELETE') THEN
	IF OLD.data_src IS NULL THEN
		perform insert_sync (OLD . ID, TG_TABLE_NAME, 'del') ;
	END IF;
ELSIF (TG_OP = 'UPDATE') THEN  
-- data_src字段必须存在
	IF NEW.data_src IS NULL THEN
-- sortnum字段不存在,直接插进去,否则sortnum有变化则不插入(因为删除时会更改sortnum)
			IF (SELECT COUNT (*) FROM information_schema. COLUMNS WHERE table_schema = TG_TABLE_SCHEMA AND TABLE_NAME = TG_TABLE_NAME AND COLUMN_NAME = 'sortnum' )=0 
			THEN 
				perform insert_sync (NEW . ID, TG_TABLE_NAME, 'upd') ;
			ELSEIF(NEW.sortnum =OLD.sortnum ) THEN
				perform insert_sync (NEW . ID, TG_TABLE_NAME, 'upd') ;
			END IF;
	END IF;  
ELSIF (TG_OP = 'INSERT') THEN
	IF NEW.data_src IS NULL THEN
		perform insert_sync (NEW . ID, TG_TABLE_NAME, 'ins') ;
	END IF;
END IF ;
 RETURN NULL ;
END ; $$ LANGUAGE plpgsql VOLATILE COST 100;
说明几点:
  • postgresql触发器函数中自带一些特殊变量,这里只解释上面函数中所用到的变量:NEW:数据类型是record。在insert、update操作触发时存储新的数据行。
    OLD:数据类型是record。在update、delete操作触发时存储旧的数据行。
    TG_OP:内容为“INSERT”、“UPDATE”、“DELETE”、“TRUNCATE”,用于指定DML语句类型。
    TG_TABLE_NAME:触发器所在表的表名称。
    TG_SCHEMA_NAME:触发器所在表的模式
  • SELECT COUNT (*) FROM information_schema. COLUMNS WHERE table_schema = TG_TABLE_SCHEMA AND TABLE_NAME = TG_TABLE_NAME AND COLUMN_NAME = 'sortnum' )=0  这句的意思是判断触发器所在的表中是否含有sortnum字段,如果等于0,说明不包含这个字段。
  • 触发器触发时调用该函数,通过判断操作类型来往dms_sync表中插入不同的数据,以update操作为例:如果data_src这个字段的值为空,再判断表中是否包含"sortnum"这个字段,如果不包含,直接调用自定义的insert_sync函数往dms_sync表中插入一条记录,如果sortnum不为空,需要判断sortnum有无变化,因为sortnum是序号,删除一条记录时,会更新其余记录的序号,就会触发触发器。

三.创建触发器

所有的准备工作都做好了,最后为需要同步的表创建触发器,以dms_dept表为例,需要创建三个触发器,分别在增删改时触发:

--------------------------
--给dms_dept创建新增触发器
-------------------------
DROP TRIGGER IF EXISTS "trigger_dms_dept_ins" on dms_dept;
CREATE TRIGGER trigger_dms_dept_ins AFTER INSERT
ON dms_dept FOR EACH ROW EXECUTE PROCEDURE trigger_insert_sync ();

--------------------------
--给dms_dept创建删除触发器
-------------------------
DROP TRIGGER IF EXISTS "trigger_dms_dept_del" on dms_dept;
CREATE TRIGGER trigger_dms_dept_del AFTER DELETE
ON dms_dept FOR EACH ROW EXECUTE PROCEDURE trigger_insert_sync ();

--------------------------
--给dms_dept创建修改触发器
-------------------------
DROP TRIGGER IF EXISTS "trigger_dms_dept_upd" on dms_dept;
CREATE TRIGGER trigger_dms_dept_upd AFTER UPDATE
ON dms_dept FOR EACH ROW EXECUTE PROCEDURE trigger_insert_sync ();

四、总结

过程是漫长而又辛酸的,结果却是短暂而又快乐的,以上的触发器与函数之间的关系可以用下图来表示:


猜你喜欢

转载自blog.csdn.net/BeauXie/article/details/77586652