首页 > 技术文章 > Mysql - 触发器/视图

elvinle 2016-12-27 10:26 原文

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

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

一、触发器

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

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

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;" 之间的东西是独立的. 所以, 如果将触发器的脚本和视图的脚本放在一个脚本中去执行, 是能够执行的.

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

 

推荐阅读