最近接触公司的一个项目,需要做一个数据同步模块,所以要用到触发器。但坑爹的是用的是postgresql数据库而非MySQL,不得不说遇到了很多的坑。本文中的内容基本上都是自己网上查找资料,参考别人的博客,再加自己摸索,才实现的,且已经用到了项目中。可能不是最优的,仅供参考,如果有更好的方法,请留言告知,谢谢!
需求说明:dms_sync是存放数据同步表,如果其他需要同步的表中记录有增删改操作,都会通过触发器往dms_sync表中增加一条数据,如dms_sync表中有以下一条记录:
这说明,用户在2017-09-02 15:39:31往dms_dept表中新插入了一条id为b22ea7c304494dfe8c5cb339930da806的记录。
postgresql的触发器需要用函数来执行,所以要实现以上的效果,还得先自定义函数。
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;
看模板不如直接看例子。。。
(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这样来调用。
触发器函数是触发器触发时调用,函数返回的类型必须是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;
说明几点:
所有的准备工作都做好了,最后为需要同步的表创建触发器,以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 ();
过程是漫长而又辛酸的,结果却是短暂而又快乐的,以上的触发器与函数之间的关系可以用下图来表示: