MySQL创建触发器实现统一主机下两个数据库的表同步
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL创建触发器实现统一主机下两个数据库的表同步相关的知识,希望对你有一定的参考价值。
具体环境:
同一主机
数据库A和B
现在需实现A数据库中表table1和B数据库中表table2插入 删除 更新同步
table1和table2表结构一致
请帮忙写下完整的触发器代码
补充:需实现双向同步,所以两边创建触发器要避免递归死循环。
create database a;
create database b;
use a
create table table1(id int, val int);
use b
create table table2(id int, val int);
然后建触发器
use a
DELIMITER //
CREATE TRIGGER tr_Insert_t1
AFTER INSERT ON table1
FOR EACH ROW
BEGIN
-- 检查当前 环境,避免递归.
IF @disable_trigger IS NULL THEN
-- 设置禁用触发器标志.
SET @disable_trigger = 1;
-- 插入目标表
INSERT INTO
b.table2
VALUES
(new.id, new.val);
-- 恢复禁用触发器标志.
SET @disable_trigger = NULL;
END IF;
END;
//
DELIMITER ;
use b
DELIMITER //
CREATE TRIGGER tr_Insert_t2
AFTER INSERT ON table2
FOR EACH ROW
BEGIN
-- 检查当前 环境,避免递归.
IF @disable_trigger IS NULL THEN
-- 设置禁用触发器标志.
SET @disable_trigger = 1;
-- 插入目标表
INSERT INTO
a.table1
VALUES
(new.id, new.val);
-- 恢复禁用触发器标志.
SET @disable_trigger = NULL;
END IF;
END;
//
DELIMITER ;
测试执行效果:
mysql> use b
Database changed
mysql> insert into table2 values(2,2);
Query OK, 1 row affected (0.01 sec)
mysql> use a
Database changed
mysql> insert into table1 values (1, 1);
Query OK, 1 row affected (0.00 sec)
mysql> select * from table1;
+------+------+
| id | val |
+------+------+
| 2 | 2 |
| 1 | 1 |
+------+------+
2 rows in set (0.00 sec)
mysql> select * from b.table2;
+------+------+
| id | val |
+------+------+
| 2 | 2 |
| 1 | 1 |
+------+------+
2 rows in set (0.00 sec)追问
delete 和 update的话 上面的代码除了 insert 还有哪些地方需要修改呢
尤其是变量名 触发器标志有什么注意事项
delete 和 update的话 , 就是 上面的触发器 定义的地方, 要修改
触发器名字要修改
触发器里面的 语句要修改.
比如
delete 的触发器里面, 要 delete 另外一个表
update的触发器里面, 要 update 另外一个表
至于那个标志 @disable_trigger
注意事项就是, 不要在别的地方
手动的执行
SET @disable_trigger = 1;
否则 所有的触发器,都要失效了。
Mysql里面好像用不了@disable_trigger 上面的sql程序 该放在哪里执行呢
追答上面的代码, 包含了 创建 数据库; 创建 表; 创建 除法器; 测试 插入的 全部代码.
代码是在 mysql-5.1.50-win32 版本下面, 测试通过的.
不知道你的 mysql 版本是什么了。
after update
of aa
ON a
referencing new as new old as old
for each row
declare
begin
if :new.aa<>:old.aa then
update B set aa=:new.aa where A.XX=B.XX;
end if;
exception
when others then
null;
end trg_A;
第5个回答 2011-09-19 一楼的插入答案可以,楼主可以先把插入看懂,试着弄。
MYSQL 批量生成触发器
上周实现银行一个需求,需求是这样的,有些项目的核心表只能通过JAVA程序进行修改,不允许其他程序修改。这个需求基于ORACLE的在去年也实现了。
大概是通过创建两个表,其中一个用来保存需要保护的核心表的名字,另外个叫黑名单的表保存禁止机器名。
然后写个过程 根据两个表生产触发器,有多少核心表,就生成多少个触发器。
上周基于ORACLE设计想法,在MYSQL上重现一次就能完成需求。
当然MYSQL的路很曲折
第一创建表结构
/*被保护的表列表*/
CREATE TABLE `PARAM_TABLE` (
`ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`TABLENAME` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_german2_ci NOT NULL COMMENT '''注意表名的大小写''',
`MODIFY_TIME` datetime DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_german2_ci
/*黑名单表 注意用户名应该输入 ‘布拉格@192.168.2.13’ 形式*/
CREATE TABLE `black_user` (
`ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`USER_NAME` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_german2_ci NOT NULL COMMENT '''username@ip''',
`MODIFY_TIME` datetime DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_german2_ci
第二 创建 存储过程
MYSQL的触发器好像不支持多个动作,本来想INSERT UPDATE DELETE合在一起,结果不行。只好分成3个来搞!
/*触发器核心程序*/
DELIMITER $$
USE `bookstore`$$
DROP PROCEDURE IF EXISTS `p_procedurecode`$$
CREATE DEFINER=`root`@`%` PROCEDURE `p_procedurecode`(IN v_schema VARCHAR(50))
BEGIN
DECLARE v_command_num INTEGER DEFAULT 1; ##触发器类型数量
DECLARE no_more_PARAM_TABLES INTEGER DEFAULT 0; ##定义游标退出变量
DECLARE v_table_name VARCHAR(100); ##定义存放表名的变量
DECLARE c_result CURSOR FOR SELECT DISTINCT TABLENAME FROM PARAM_TABLE; ##定义游标的SQL
DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_PARAM_TABLES=1; ##关联游标NOT FOUD变量##如果没有找到数据则设置该变量为1
OPEN c_result;
WHILE no_more_PARAM_TABLES <> 1 DO ##第一层 外层游标循环 这里要判断退出标志
FETCH c_result INTO v_table_name; ##提取游标结果
IF no_more_PARAM_TABLES <> 1 THEN ##第二次判断 游标是否可退出
SET v_command_num = 1; ##如果有数据则进入内循环
WHILE v_command_num < 4 DO ##内循环退出标志
SET @sql='DELIMITER $$ '; ##很烦这个 为啥不像ORACLE学习呢?
SET @sql=CONCAT(@sql,CHAR(10)); ##CONCAT是字符串拼接 不如ORACLE | 符合方便
SET @sql=CONCAT(@sql,'USE `');
SET @sql=CONCAT(@sql,v_schema);
SET @sql=CONCAT(@sql,'`$$');
SET @sql=CONCAT(@sql,CHAR(10)); ##CHAR 10是换行符号
IF v_command_num = 1 THEN ## 判断触发器类型生成不同的触发器名字
SET @sql=CONCAT(@sql,'DROP TRIGGER `tr_protect_u_');
END IF;
IF v_command_num = 2 THEN
SET @sql=CONCAT(@sql,'DROP TRIGGER `tr_protect_i_');
END IF;
IF v_command_num = 3 THEN
SET @sql=CONCAT(@sql,'DROP TRIGGER `tr_protect_d_');
END IF;
SET @sql=CONCAT(@sql,v_table_name);
SET @sql=CONCAT(@sql,'`$$');
SET @sql=CONCAT(@sql,CHAR(10));
SET @sql=CONCAT(@sql,'CREATE TRIGGER ');
SET @sql=CONCAT(@sql, v_schema);
IF v_command_num = 1 THEN
SET @sql=CONCAT(@sql,'.`tr_protect_u_');
END IF;
IF v_command_num = 2 THEN
SET @sql=CONCAT(@sql,'.`tr_protect_i_');
END IF;
IF v_command_num = 3 THEN
SET @sql=CONCAT(@sql,'.`tr_protect_d_');
END IF;
SET @sql=CONCAT(@sql, v_table_name);
IF v_command_num = 1 THEN
SET @sql=CONCAT(@sql,'` BEFORE UPDATE ON ');
END IF;
IF v_command_num = 2 THEN
SET @sql=CONCAT(@sql,'` BEFORE INSERT ON ');
END IF;
IF v_command_num = 3 THEN
SET @sql=CONCAT(@sql,'` BEFORE DELETE ON ');
END IF;
##下面是拼接触发器核心内容,大意是判断当前用户是否在黑名单中
SET @sql=CONCAT(@sql, v_schema);
SET @sql=CONCAT(@sql,'.');
SET @sql=CONCAT(@sql,v_table_name);
SET @sql=CONCAT(@sql,CHAR(10));
SET @sql=CONCAT(@sql,' FOR EACH ROW BEGIN');
SET @sql=CONCAT(@sql,CHAR(10));
SET @sql=CONCAT(@sql,' set @count=(SELECT COUNT(*) FROM ');
SET @sql=CONCAT(@sql, v_schema);
SET @sql=CONCAT(@sql,'.black_user WHERE user_name = USER());');
SET @sql=CONCAT(@sql,CHAR(10));
SET @sql=CONCAT(@sql,' IF @count>0 THEN ');
SET @sql=CONCAT(@sql,CHAR(10));
SET @sql=CONCAT(@sql,' SIGNAL SQLSTATE ''48000'' SET MESSAGE_TEXT = ''您没有权限操作参数表!'';' );
SET @sql=CONCAT(@sql,CHAR(10));
SET @sql=CONCAT(@sql,' END IF;');
SET @sql=CONCAT(@sql,CHAR(10));
SET @sql=CONCAT(@sql,' END; ');
SET @sql=CONCAT(@sql,CHAR(10));
SET @sql=CONCAT(@sql,' $$');
SET @sql=CONCAT(@sql,CHAR(10));
SET @sql=CONCAT(@sql,'DELIMITER ;');
SELECT @sql; ##由于MYSQL不支持动态执行CREATE TRRIGE的DDL语句 只能先打印出来
SET v_command_num = v_command_num +1;
END WHILE;
END IF;
END WHILE;
CLOSE C_RESULT;
END$$
DELIMITER ;
写这个存储过程时 感觉比ORACLE 繁琐。
1 不同动态执行SQL FOR DDL
2 DELIMITER 好烦
3 没有FOR CUR_XX () LOOP 语句
4 UNITE REPLACE 循环会多循环一次,不得不采用两个WHILE来循环游标
5 @变量 和自定义变量 感觉有很大的区别,实际没啥区别。
总得来说MSQL 开发对开发人员不太友好。也就是用户友好性不够好
第三执行 存储过程
不能动态执行,只好先把执行结果,就是打印DDL语句到LINUX 文件中
这里要用到MYSQL客户端 -E 参数 来执行SQL。然后执行导出来的DDL.SQL
#/bin/bash
#write by ZengFanKun doctor
#on 2021-02-28
USER=root
PWD=123456
#HOST=192.168.2.13
HOST=localhost
PROT=3306
DATABASE=bookstore
MYSQL_PATH=/app/mysql8/mysql8_soft
cd ${MYSQL_PATH}
./bin/mysql -uroot -p123456 -hlocalhost -P3306 -D${DATABASE} --vertical --skip-column-names -e"CALL p_procedurecode('${DATABASE}')" |grep -v row > create_trigger_ddl.sql
./bin/mysql -uroot -p123456 -hlocalhost -P3306 -D${DATABASE}
--force < create_trigger_ddl.sql
--vertical ##垂直显示结果 类似 \G
--skip-column-names ##不显示 字段名称 类似SQLPLUS 不显示标题
-e"CALL p_procedurecode('${DATABASE}')" ##执行存储过程
grep -v row > create_trigger_ddl.sql ##过滤掉 -----1row----
忽悠该类型的错误和警告
[root@centos7 mysql8]# sh make_tr_ddl.sh
mysql: [Warning] Using a password on the command line interface can be insecure.
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 1360 (HY000) at line 3: Trigger does not exist
ERROR 1360 (HY000) at line 15: Trigger does not exist
ERROR 1360 (HY000) at line 27: Trigger does not exist
ERROR 1360 (HY000) at line 39: Trigger does not exist
ERROR 1360 (HY000) at line 51: Trigger does not exist
ERROR 1360 (HY000) at line 63: Trigger does not exist
本来是使用SHELL变量来登录的,不管如何折腾发现MYSQL报错
说 'ROOT'@'LOCALHOST' 拒绝登陆。只好让开发人员自己去填实参
执行脚本需要手工输入数据库的执行用户名,密码,端口,路径,SCHEMA
我用SQLyog 工具无法调试MYSQL的存储过程!
以上是关于MySQL创建触发器实现统一主机下两个数据库的表同步的主要内容,如果未能解决你的问题,请参考以下文章