Mysql

触发器在之前的项目中, 应用的着实不多, 没有办法的时候, 才会去用这个. 因为这个东西在后期并不怎么好维护, 也容易造成紊乱.

我最近的项目中, 由于数据库设计(别人设计的)原因, 导致一些最简单功能, 查询起来, 都很麻烦和复杂. 牵涉表非常多, 表与表之间又互有部分关系. 我想说, 这是我见过的最糟糕的数据库设计了. 最后没办法, 公司架构师给了触发器的解决方案.

一、触发器

在项目中, 我新建了一张关系表, 把一些必要的, 有效的关系, 通过触发器的方式, 更新到一张表中, 并在这张表里面建了索引. 然后读取数据的时候, 就通过连接这张关系表, 去得到最后的有效数据. 看上去, 有点类似于读写分离的赶脚, 不过这并不是多台数据库服务器间的.

由于工作的关系, 我不能使用项目中的数据库来做记录, 那就自己搞几个表来玩玩吧. 先建三张表

CREATE TABLE `tch_teacher` (
    `Id` INT (11) NOT NULL AUTO_INCREMENT,
    `Sex` SMALLINT (6) DEFAULT NULL,
    `BId` VARCHAR (36) DEFAULT NULL,
    `No` VARCHAR (20) DEFAULT NULL,
    `Name` VARCHAR (30) DEFAULT NULL,
    `IsDeleted` bit (1) DEFAULT b '0' PRIMARY KEY (`Id`),
    KEY `Index_Sex` (`Sex`) USING BTREE,
    KEY `Index_BId` (`BId`) USING BTREE
) ENGINE = INNODB AUTO_INCREMENT = 21 DEFAULT CHARSET = latin1;

CREATE TABLE `tch_contact` (
    `Id` INT (11) NOT NULL AUTO_INCREMENT,
    `TId` INT (11) DEFAULT NULL,
    `QQ` VARCHAR (15) DEFAULT NULL,
    `Weixin` VARCHAR (50) DEFAULT NULL,
    `Phone` VARCHAR (15) DEFAULT NULL,
    PRIMARY KEY (`Id`),
    KEY `Index_TId` (`TId`) USING BTREE
) ENGINE = INNODB AUTO_INCREMENT = 11 DEFAULT CHARSET = latin1 COMMENT = '联系方式表';

CREATE TABLE tch_all (
    Id INT NOT NULL,
    Sex SMALLINT,
    BId VARCHAR (36),
    NO VARCHAR (20),
    NAME VARCHAR (30),
    QQ VARCHAR (15),
    Weixin VARCHAR (50),
    Phone VARCHAR (15)
) COMMENT '完整表';

我这里就通过触发器的方式, 来维护tch_all这张表. 例子不好, 主要是介绍功能, 见谅.

delimiter $
drop trigger if EXISTS tg_insert_all;
create trigger tg_insert_all after insert on tch_teacher for each ROW
BEGIN
  insert into tch_all(Sex,BId,NO,NAME) values(new.sex, new.bid, new.no, new.name);
end $
delimiter;

1. 语法

create trigger 触发器名 before/after insert/update/delete on 表名 for each row

begin

end

1. 触发时机 before/after

这里的触发器, 触发的时机是在tch_teacher表数据插入之后. 也就是说, tch_teacher插入成功了之后, 才会向tch_all表插入数据. 这里有一个点需要注意下. 在tch_teacher插入成功后, 向tch_all插入的时候报错, 那么tch_teacher的新插数据就回被回滚.

有插入后触发, 自然就有插入前触发, 只需要将after改成before即可. 

before触发, 则会先想tch_all插入数据, 再向tch_teacher插入数据. 插入过程中, 不管哪一步失败, 都会回滚数据. 所以不需要担心, 触发不成功的情况下, 会不会造成冗余或者错误数据.

2. 触发方式 insert/update/delete

触发方式, 有插入/修改/删除 时触发. 例子中我只写了插入触发别的两种方式的使用方法是和这个一样的.

3. 原数据引用  old/new

这里有一个问题, 我修改了数据, 那么我怎么引用他们呢? 既然修改了数据, 那肯定是有 修改前数据和修改后新数据 的引用的, 

这里直接使用 old 来指向修改前的数据, new 指向修改后的数据. 这里的指向, 是指向的tch_teacher中的数据, 不是tch_all的数据.

4. 注

例子中, 我只用了一句话, 例子嘛, 简单就好. 其实在实际使用过程之中, 不会是这么简单的. 举个例子说吧.

很多时候, 由于数据重要性, 不会直接删除数据. 而是选择更新数据状态来表示其已不再使用. 这里就用 isdeleted来表示, 0表示能用, 1表示不再使用.

当我更新tch_teacher的isdeleted的值为1的时候, 触发修改触发器, 在触发器中, 我就需要判断 new.isdeleted的值, 从而选择是否删除关系表中的数据.

delimiter $
DROP TRIGGER IF EXISTS tg_update_all ; 
CREATE TRIGGER tg_update_all AFTER UPDATE ON tch_teacher FOR EACH ROW
BEGIN
IF new.isdeleted = 1 THEN
    DELETE FROM tch_all WHERE id = old.id ;
ELSE
    UPDATE tch_all set sex=new.sex, bid=new.bid, NO=new.NO, NAME=new.NAME where id = old.id;
END IF ;
END$
delimiter ;

到这里, 我发现好像没有继续这个例子的必要了, 好吧, 那就这样了.

二、视图

视图的作用: 简化查询, 提升查询速度.

老版本的mysql, 并不支持视图子查询, 但是新版本的mysql, 已经能支持了.

就上面这个例子而言, 其实也可以使用视图的方式, 去解决复杂的逻辑.

单是就查询性能上来说, 我觉得还是触发器的方式快一些. 毕竟触发器维护了一张新表, 而且新表能够建索引来提升查询速度. 就是维护起来比较麻烦.

delimiter $
drop view if EXISTS v_all; -- 删除视图
create view v_all AS    -- 新建视图
select tch_teacher.*,tch_contact.QQ,tch_contact.Weixin,tch_contact.Phone from tch_teacher 
left join tch_contact on tch_teacher.Id=tch_contact.TId where tch_teacher.IsDeleted=0 $
delimiter;

这里我使用到了一个东西:delimiter, 这个在mysql中, 是用来分割的. 

"delimiter $" 到 "$ delimiter;" 之间的东西是独立的. 所以, 如果将触发器的脚本和视图的脚本放在一个脚本中去执行, 是能够执行的.

如果是单个执行, 就不需要加那个了. 

原文地址:https://www.cnblogs.com/elvinle/p/6224930.html