• 013:Rank、视图、触发器、MySQL内建函数


    一. Rank

    给出不同的用户的分数,然后根据分数计算排名

    (gcdb@localhost) 09:34:47 [mytest]> create table t_rank(id int,score int);
    Query OK, 0 rows affected (0.02 sec)
    
    (gcdb@localhost) 10:13:03 [mytest]> insert into t_rank values(1, 10), (2, 20), (3, 30), (4, 30), (5, 40), (6, 40);
    Query OK, 6 rows affected (0.00 sec)
    Records: 6  Duplicates: 0  Warnings: 0
    
    (gcdb@localhost) 10:13:13 [mytest]> select * from t_rank;
    +------+-------+
    | id   | score |
    +------+-------+
    |    1 |    10 |
    |    2 |    20 |
    |    3 |    30 |
    |    4 |    30 |
    |    5 |    40 |
    |    6 |    40 |
    +------+-------+
    6 rows in set (0.00 sec)
    
    (gcdb@localhost) 10:13:24 [mytest]>
    
    
    (gcdb@localhost) 10:21:54 [mytest]> SET @prev_value := NUll;
    Query OK, 0 rows affected (0.00 sec)
    -- 假设比较到第N行,设置一个变量prev_value用于存放第N-1行score的分数
    -- 用于比较第N行的score和第N-1行的score
    -- prev_value可以理解为 是临时保存第N-1行的score的变量
    
    (gcdb@localhost) 10:25:38 [mytest]> set @rank_count := 0;     -- 用于存放当前的排名
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 10:25:38 [mytest]> select  id, score, 
        -> case
        -> when @prev_value = score then @rank_count  
           -- 相等则prev_value不变, 并返回rank_count(第一次为NULL,不会相等,所以跳转到下一个when语句)
        -> when @prev_value := score then @rank_count := @rank_count + 1 
           -- 不等,则第N行的score赋值(:=)给prev_value。且rank_count增加1
        -> end as rank_column   -- case 开始的,end结尾
        -> from t_rank
        -> order by score desc;
    +------+-------+-------------+
    | id   | score | rank_column |
    +------+-------+-------------+
    |    5 |    40 |           1 |
    |    6 |    40 |           1 |
    |    3 |    30 |           2 |
    |    4 |    30 |           2 |
    |    2 |    20 |           3 |
    |    1 |    10 |           4 |
    +------+-------+-------------+
    6 rows in set (0.00 sec)
    
    -- case  
    --   when [condition_1] then [do_something_1] 
    --   when [condition_2] then [do_something_2] 
    --   end
    --	语法:  如果 condition_1条件满足,则执行 do_something_1 然后就跳出,不会执行condition_2;
    --			如果 condition_1条件不满足,则继续执行到 condition_2。以此类推。
    

    -上面语句一句编写

    "select  id, score, 
    case
      when @prev_value = score then @rank_count          
      when @prev_value := score then @rank_count := @rank_count + 1            
    end as rank_column
    from t_rank  t,
    (SELECT @prev_value := NUll,@rank_count:= 0 ) a
    order by score desc;"
    
    (gcdb@localhost) 10:26:57 [mytest]> select  id, score,
        -> case
        ->  when @prev_value = score then @rank_count
        ->  when @prev_value := score then @rank_count := @rank_count + 1
        -> end as rank_column
        -> from t_rank  t,
        -> (SELECT @prev_value := NUll,@rank_count:= 0 ) a  --把@prev_value和@rank_count放到子查询里面
        -> order by score desc;
    +------+-------+-------------+
    | id   | score | rank_column |
    +------+-------+-------------+
    |    5 |    40 |           1 |
    |    6 |    40 |           1 |
    |    3 |    30 |           2 |
    |    4 |    30 |           2 |
    |    2 |    20 |           3 |
    |    1 |    10 |           4 |
    +------+-------+-------------+
    6 rows in set (0.00 sec)
    
    (gcdb@localhost) 11:06:00 [mytest]>
    
    

    rank参考资料


    二. 视图

    官方view文档

    2.1、创建视图

    --
    -- 创建视图,视图名v_rank
    --
    (gcdb@localhost) 11:34:40 [mytest]> create view v_rank as select * from t_rank;  --对select结果增加条件进行过滤后,再创建视图
    Query OK, 0 rows affected (0.04 sec)
    
    (gcdb@localhost) 11:35:08 [mytest]> show create table v_rank G;   --查看视图表结构
    *************************** 1. row ***************************
                    View: v_rank
             Create View: CREATE ALGORITHM=UNDEFINED DEFINER=`gcdb`@`%` SQL SECURITY DEFINER VIEW `v_rank` AS select `t_rank`.`id` AS `id`,`t_rank`.`score` AS `score` from `t_rank`
    character_set_client: utf8
    collation_connection: utf8_general_ci    --显示的是视图的定义
    1 row in set (0.00 sec)
    
    ERROR:
    No query specified
    
    (gcdb@localhost) 11:35:48 [mytest]> show create table t_rank G;  --查看原表结构
    *************************** 1. row ***************************
           Table: t_rank
    Create Table: CREATE TABLE `t_rank` (
      `id` int(11) DEFAULT NULL,
      `score` int(11) DEFAULT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    1 row in set (0.00 sec)
    
    ERROR:
    No query specified
    
    (gcdb@localhost) 11:35:59 [mytest]> select * from v_rank;   -- 可以直接查询该视图得结果
    +------+-------+
    | id   | score |
    +------+-------+
    |    1 |    10 |
    |    2 |    20 |
    |    3 |    30 |
    |    4 |    30 |
    |    5 |    40 |
    |    6 |    40 |
    +------+-------+
    6 rows in set (0.00 sec)
    
    -- 视图的作用是,可以对开发人员是透明的,屏蔽部分敏感的列
    -- 视图在mysql是虚拟表。根据视图的定义,还是取执行定义中的select语句。
    
    -- 只开放部分列
    (gcdb@localhost) 11:40:35 [mytest]> create view v_rank_01 as select id from t_rank; -- 只开放id列
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 11:42:50 [mytest]> select * from v_rank_01;  -- 即使 select * ,也只能看到id列,具有隐藏原来表中部分列的功能                        
    +------+
    | id   |
    +------+
    |    1 |
    |    2 |
    |    3 |
    |    4 |
    |    5 |
    |    6 |
    +------+
    6 rows in set (0.00 sec)
    
    -- 不要取用select * from 去创建视图,因为mysql会把*逐个解析成列。
    -- 当原来的表结构发生变化时,视图的表结构是不会发生变化的,视图在创建的瞬间,便确定了结构。
    -- 比如,当你alter原来的表 增加列(add columns)时,再去查询该视图,新增加的列是不存在的。
    
    (gcdb@localhost) 11:43:20 [mytest]> alter table t_rank add column c int default 0;  -- 增加一列名字为c,默认值为0
    Query OK, 0 rows affected (0.06 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    
    (gcdb@localhost) 11:44:59 [mytest]> select * from t_rank;   -- 查询原表
    +------+-------+------+
    | id   | score | c    |
    +------+-------+------+
    |    1 |    10 |    0 |
    |    2 |    20 |    0 |
    |    3 |    30 |    0 |
    |    4 |    30 |    0 |
    |    5 |    40 |    0 |
    |    6 |    40 |    0 |
    +------+-------+------+
    6 rows in set (0.00 sec)
    
    (gcdb@localhost) 11:45:08 [mytest]> select * from v_rank;  -- 尽管view_rank用select * 创建,但当时没有列c,所以无法得到c列的值
    +------+-------+
    | id   | score |
    +------+-------+
    |    1 |    10 |
    |    2 |    20 |
    |    3 |    30 |
    |    4 |    30 |
    |    5 |    40 |
    |    6 |    40 |
    +------+-------+
    6 rows in set (0.00 sec)
    
    (gcdb@localhost) 11:49:35 [mytest]> drop view v_rank_01; --删除视图
    Query OK, 0 rows affected (0.01 sec)
    
    
    -- 注意:mysql中的视图都是虚拟表。不像Oracle可以物化成真实存在的表。
    --      每次查询视图,实际上还是去查询的原来的表,只是查询的规则是在视图创建时经过定义的。
    
    

    2.2、视图的算法

    • 视图的算法(ALGORITHM)有三种方式:
      • UNDEFINED : 默认方式,由MySQL来判断使用下面的哪种算法
      • MERGE每次通过物理表查询得到结果,把结果merge(合并)起来返回
      • TEMPTABLE : 产生一张临时表,把数据放入临时表后,客户端再去临时表取数据(不会缓存

    TEMPTABLE 特点 :即使访问条件一样,第二次查询还是会去读取物理表中的内容,并重新生成一张临时表,并不会取缓存之前的表。(临时表是Memory存储引擎,默认放内存,超过配置大小放磁盘)
    当查询有一个较大的结果集时,使用TEMPTABLE可以快速的结束对该物理表的访问,从而可以快速释放这张物理表上占用的资源。然后客户端可以对临时表上的数据做一些耗时的操作,而不影响原来的物理表。所以一般我们使用默认的UNDEFINED,由MySQL自己去判断


    三. 触发器

    官方trigger文档

    3.1、触发器介绍

    • 触发器定义

      • 触发器的对象是,当表上出现特定的事件触发该程序的执行
    • 触发器的类型

      • UPDATE

        • update 操作
      • DELETE

        • delete 操作
        • replace 操作
          • 注意:drop,truncate等DDL操作不会触发DELETE
      • INSERT

        • insert 操作
        • load data 操作
        • replace 操作

    注意:replace操作会触发两次,一次是UPDATE类型的触发器,一次是INSERT类型的触发器

    MySQL 5.6版本同一个类型的触发器只能有一个(单个表)

    MySQL 5.7允许多个同一类型的触发器

    触发器只触发DML(Data Manipulation Language)操作,不会触发DDL(Data Definition Language)操作 (create,drop等操作)

    3.2、触发器语法

    • 创建触发器
    CREATE
        [DEFINER = { user | CURRENT_USER }]
        TRIGGER trigger_name  -- 触发器名字
        trigger_time trigger_event  -- 触发时间和事件
        ON tbl_name FOR EACH ROW    
        [trigger_order]
        trigger_body
    
    trigger_time: { BEFORE | AFTER }   -- 事件之前还是之后触发
    trigger_event: { INSERT | UPDATE | DELETE }  -- 三个类型
    trigger_order: { FOLLOWS | PRECEDES } other_trigger_name
    
    • trigger_name:标识触发器名称,用户自行指定;
    • trigger_time:标识触发时机,取值为 BEFORE 或 AFTER;
    • trigger_event:标识触发事件,取值为 INSERT、UPDATE 或 DELETE;
    • tbl_name:标识建立触发器的表名,即在哪张表上建立触发器;
    • trigger_stmt:触发器程序体,可以是一句SQL语句,或者用 BEGIN 和 END 包含的多条语句。
    • trigger_order:值为FOLLOWS 或者 PRECEDES 后面跟上现有的触发器的名字(注意:这两个触发器的触发条件和触发时间必须一样)。如果为FOLLOWS,这个新的触发器就会在现有的触发器之后被触发。如果为PRECEDES,就会在现有的触发器之前执行。
    • trigger_body:触发器的程序体

    由此可见,可以建立6种触发器,即:BEFORE INSERT、BEFORE UPDATE、BEFORE DELETE、AFTER INSERT、AFTER UPDATE、AFTER DELETE

    另外有一个限制是5.7.2之前不能同时在一个表上建立2个相同类型的触发器。

    3.3、UPDATE 类型触发器

    (gcdb@localhost) 11:49:38 [mytest]> create table t_trigger(name varchar(32),score int(10),primary key(name));
    Query OK, 0 rows affected (0.01 sec)
    
    (gcdb@localhost) 12:10:21 [mytest]> insert into t_trigger  values('fanghao',88),('caowei',59),('xuliuyann',93);
    Query OK, 3 rows affected (0.00 sec)
    Records: 3  Duplicates: 0  Warnings: 0
    
    (gcdb@localhost) 13:46:20 [mytest]> select * from t_trigger;
    +----------+-------+
    | name     | score |
    +----------+-------+
    | caowei   |    59 |
    | fanghao  |    88 |
    | xuliuyan |    93 |
    +----------+-------+
    3 rows in set (0.00 sec) 
    (gcdb@localhost) 14:00:10 [mytest]> delimiter //  -- 将语句分隔符定义设置为 // (原来是';')
    
    (gcdb@localhost) 14:00:44 [mytest]> create trigger trg_update_score   -- 定义触发器名字
        -> before update on t_trigger                                     -- 作用在test_trigger_1 更新(update)之前(before)        
        -> for each row                                                   -- 每行
        -> begin                                                          -- 开始定义
        -> if new.score < 60 then set new.score=60;                       -- 如果新值小于60,则设置为60
        -> elseif new.score > 100 then set new.score=100;                 -- 如果新值大于100,则设置为100
        -> end if;                                                        -- if 对应结束 
        -> end;                                                          -- begin 对应结束 
        -> //
    Query OK, 0 rows affected (0.01 sec)
    (gcdb@localhost) 14:11:52 [mytest]> delimiter ;    -- 将语句分隔符定义设置为 ';' 结束符
    
    (gcdb@localhost) 14:02:21 [mytest]> show triggers from mytest G;
    *************************** 1. row ***************************
                 Trigger: trg_update_score
                   Event: UPDATE                         --定义为update类型
                   Table: t_trigger
               Statement: if new.score < 60 then set new.score=60;
    elseif new.score > 100 then set new.score=100;
    end if
                  Timing: BEFORE
                 Created: 2017-12-09 14:00:45.45
                sql_mode:
                 Definer: gcdb@%
    character_set_client: utf8
    collation_connection: utf8_general_ci
      Database Collation: utf8_general_ci
    1 row in set (0.00 sec)
    
    ERROR:
    No query specified
    (gcdb@localhost) 14:21:41 [mytest]> insert into t_trigger values('tom',55);   -- 插入tom,分数55
    Query OK, 1 row affected (0.00 sec)
    
    (gcdb@localhost) 14:21:55 [mytest]> select * from t_trigger;
    +------+-------+
    | name | score |
    +------+-------+
    | tom  |    55 |
    +------+-------+
    1 row in set (0.00 sec)
    
    (gcdb@localhost) 14:21:58 [mytest]> update t_trigger set score=58 where name = 'tom';  --更新tom分数为58
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    
    (gcdb@localhost) 14:22:09 [mytest]> select * from t_trigger; 
    +------+-------+
    | name | score |
    +------+-------+
    | tom  |    60 |   --因为update类型,触发了触发器,score值小60设置为60
    +------+-------+
    1 row in set (0.00 sec)
    
    (gcdb@localhost) 14:22:11 [mytest]> insert into t_trigger values('sim',111);  -- 插入sim,分数111
    Query OK, 1 row affected (0.00 sec)
    
    (gcdb@localhost) 14:22:26 [mytest]> update t_trigger set score=2222 where name = 'sim';  -- 插入sim,分数2222,触发了触发器,score值大于100设置为100
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    
    (gcdb@localhost) 14:22:39 [mytest]> select * from t_trigger;
    +------+-------+
    | name | score |
    +------+-------+
    | sim  |   100 |  
    | tom  |    60 |
    +------+-------+
    2 rows in set (0.00 sec)
    
    (gcdb@localhost) 14:22:42 [mytest]> update t_trigger set score=99 where name = 'sim'; --更新sim分数为99,触发了触发器但是,分数在60< score <100 之间,不符合更改条件,未设置,还是99
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    
    (gcdb@localhost) 14:22:49 [mytest]> select * from t_trigger;
    +------+-------+
    | name | score |
    +------+-------+
    | sim  |    99 |
    +------+-------+
    2 rows in set (0.00 sec)
    
    (gcdb@localhost) 14:22:50 [mytest]>
    
    

    3.4、INSERT 类型触发器

    *创建两张测试表

    (gcdb@localhost) 14:44:36 [mytest]> create table t_teachar(tid varchar(30) primary key,tpasswd varchar(32) not null);
    Query OK, 0 rows affected (0.01 sec)
    
    (gcdb@localhost) 14:46:57 [mytest]> create table t_user(uid varchar(30) primary key,upasswd varchar(32) not null);
    Query OK, 0 rows affected (0.01 sec)
    
    (gcdb@localhost) 14:56:56 [mytest]> show create table t_user G;
    *************************** 1. row ***************************
           Table: t_user
    Create Table: CREATE TABLE `t_user` (
      `uid` varchar(30) NOT NULL,
      `upasswd` varchar(32) NOT NULL,
      PRIMARY KEY (`uid`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    1 row in set (0.00 sec)
    
    ERROR:
    No query specified
    
    (gcdb@localhost) 14:57:09 [mytest]> show create table t_terchar G;
    *************************** 1. row ***************************
           Table: t_terchar
    Create Table: CREATE TABLE `t_terchar` (
      `tid` varchar(30) NOT NULL,
      `tpasswd` varchar(32) NOT NULL,
      PRIMARY KEY (`tid`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    1 row in set (0.00 sec)
    
    ERROR:
    No query specified
    
    (gcdb@localhost) 15:13:40 [mytest]> DELIMITER //
    (gcdb@localhost) 15:14:02 [mytest]> CREATE TRIGGER trg_insert_after_teachar
        -> AFTER INSERT ON t_teachar
        -> FOR EACH ROW
        -> BEGIN
        -> INSERT t_user(uid, upasswd) VALUES(NEW.tid, NEW.tpasswd);   --在t_teachar表插入语句之后也在t_user表插入语句
        -> END
        -> //
    Query OK, 0 rows affected (0.01 sec)
    (gcdb@localhost) 15:14:02 [mytest]> DELIMITER ;
    
    (gcdb@localhost) 15:14:09 [mytest]> show triggers G;
    *************************** 1. row ***************************
                 Trigger: trg_insert_after_teachar
                   Event: INSERT
                   Table: t_teachar
               Statement: BEGIN
      INSERT t_user(uid, upasswd) VALUES(NEW.tid, NEW.tpasswd);
    END
                  Timing: AFTER
                 Created: 2017-12-09 15:14:02.07
                sql_mode:
                 Definer: gcdb@%
    character_set_client: utf8
    collation_connection: utf8_general_ci
      Database Collation: utf8_general_ci
    
    (gcdb@localhost) 15:14:19 [mytest]> insert into t_teachar values('1','aaaa');  --在t_teachar表里插入语句
    Query OK, 1 row affected (0.00 sec)
    
    (gcdb@localhost) 15:14:44 [mytest]> select * from t_user; --在t_user表里面可以看
    +-----+---------+
    | uid | upasswd |
    +-----+---------+
    | 1   | aaaa    |
    +-----+---------+
    1 row in set (0.00 sec)
    
    (gcdb@localhost) 15:14:56 [mytest]>
    
    

    3.5、Delete 类型触发器

    (gcdb@localhost) 16:01:00 [mytest]> drop table t_user;
    Query OK, 0 rows affected (0.01 sec)
    
    (gcdb@localhost) 16:04:02 [mytest]> create table t_user(uid varchar(30) primary key,upasswd varchar(32) not null,score int,time timestamp(6) not null default current_timestamp(6) on updatecurrent_timestamp(6));
    Query OK, 0 rows affected (0.01 sec)
    
    (gcdb@localhost) 16:04:20 [mytest]> drop table t_teachar;
    Query OK, 0 rows affected (0.03 sec)
    
    (gcdb@localhost) 16:04:40 [mytest]> create table t_teachar(tid varchar(30) primary key,tpasswd varchar(32) not null,score int,time timestamp(6) not null default current_timestamp(6) on update current_timestamp(6));
    Query OK, 0 rows affected (0.24 sec)
    
    (gcdb@localhost) 16:28:37 [mytest]> insert into t_teachar values('1','aaaa',77,null);
    Query OK, 1 row affected (0.00 sec)
    
    (gcdb@localhost) 16:30:29 [mytest]> insert into t_teachar values('2','bbbb',88,null);
    Query OK, 1 row affected (0.00 sec)
    
    (gcdb@localhost) 16:30:29 [mytest]> insert into t_teachar values('3','cccc',99,null);
    Query OK, 1 row affected (0.00 sec)
    
    (gcdb@localhost) 16:30:46 [mytest]> insert into t_user values('1','aaaa',77,null);
    Query OK, 1 row affected (0.00 sec)
    
    (gcdb@localhost) 16:31:36 [mytest]> insert into t_user  values('2','bbbb',88,null);
    Query OK, 1 row affected (0.00 sec)
    
    (gcdb@localhost) 16:31:36 [mytest]> insert into t_user values('3','cccc',99,null);
    Query OK, 1 row affected (0.00 sec)
    
    (gcdb@localhost) 16:34:35 [mytest]> DELIMITER //
    (gcdb@localhost) 16:35:23 [mytest]> CREATE TRIGGER trg_delete_teachar
        -> AFTER DELETE ON t_teachar
        -> FOR EACH ROW
        -> BEGIN
        -> DELETE FROM t_user WHERE uid = OLD.Tid;
        -> END
        -> //
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 16:35:23 [mytest]> DELIMITER ;
    (gcdb@localhost) 16:35:24 [mytest]> select * from t_teachar;
    +-----+---------+-------+----------------------------+
    | tid | tpasswd | score | time                       |
    +-----+---------+-------+----------------------------+
    | 1   | aaaa    |    77 | 2017-12-09 16:30:29.668864 |
    | 2   | bbbb    |    88 | 2017-12-09 16:30:29.669887 |
    | 3   | cccc    |    99 | 2017-12-09 16:30:30.885201 |
    +-----+---------+-------+----------------------------+
    3 rows in set (0.00 sec)
    
    (gcdb@localhost) 16:35:35 [mytest]>  select * from t_user;
    +-----+---------+-------+----------------------------+
    | uid | upasswd | score | time                       |
    +-----+---------+-------+----------------------------+
    | 1   | aaaa    |    77 | 2017-12-09 16:31:36.076287 |
    | 2   | bbbb    |    88 | 2017-12-09 16:31:36.076968 |
    | 3   | cccc    |    99 | 2017-12-09 16:31:37.350546 |
    +-----+---------+-------+----------------------------+
    3 rows in set (0.00 sec)
    
    (gcdb@localhost) 16:35:40 [mytest]> delete from t_teachar  where tid=1;
    Query OK, 1 row affected (0.00 sec)
    
    (gcdb@localhost) 16:35:49 [mytest]>  select * from t_user;
    +-----+---------+-------+----------------------------+
    | uid | upasswd | score | time                       |
    +-----+---------+-------+----------------------------+
    | 2   | bbbb    |    88 | 2017-12-09 16:31:36.076968 |
    | 3   | cccc    |    99 | 2017-12-09 16:31:37.350546 |
    +-----+---------+-------+----------------------------+
    2 rows in set (0.00 sec)
    
    (gcdb@localhost) 16:35:51 [mytest]>  select * from t_teachar;
    +-----+---------+-------+----------------------------+
    | tid | tpasswd | score | time                       |
    +-----+---------+-------+----------------------------+
    | 2   | bbbb    |    88 | 2017-12-09 16:30:29.669887 |
    | 3   | cccc    |    99 | 2017-12-09 16:30:30.885201 |
    +-----+---------+-------+----------------------------+
    2 rows in set (0.00 sec)
    
    (gcdb@localhost) 16:36:05 [mytest]>
    
    

    3.6、显示和删除触发器

    (gcdb@localhost) 16:47:15 [mytest]> show triggers G;
    *************************** 1. row ***************************
                 Trigger: trg_delete_teachar
                   Event: DELETE
                   Table: t_teachar
               Statement: BEGIN
    DELETE FROM t_user WHERE uid = OLD.Tid;
    END
                  Timing: AFTER
                 Created: 2017-12-09 16:35:23.60
                sql_mode:
                 Definer: gcdb@%
    character_set_client: utf8
    collation_connection: utf8_general_ci
      Database Collation: utf8_general_ci
    1 row in set (0.00 sec)
    
    ERROR:
    No query specified
    
    (gcdb@localhost) 16:47:46 [mytest]> drop  trigger trg_delete_teachar;
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 16:48:24 [mytest]> show triggers G;
    Empty set (0.00 sec)
    
    ERROR:
    No query specified
    
    (gcdb@localhost) 16:48:28 [mytest]>
    

    3.7、触发器总结

    • 触发器对性能有损耗,应当非常慎重使用;

    • 对于事物表,触发器执行失败则整个语句回滚

    • Row格式主从复制,触发器不会在从库上执行

      • 因为从库复制的肯定是主库已经提交的数据,既然已经提交了说明触发器已经被触发过了,所以从库不会执行。
    • 使用触发器时应防止递归执行;

      delimiter //
      create trigger trg_test
          before update on 'test_trigger'
          for each row
      begin
          update test_trigger set score=20 where name = old.name;  -- 又触发了update操作,循环触发了
      end;//
      

    3.8、触发器模拟物化视图

    • 物化视图的概念

      • 不是基于基表的虚表
      • 根据基表实际存在的实表
      • 预先计算并保存耗时较多的SQL操作结果(如多表链接(join)或者group by等)
    • 模拟物化视图

    (root@localhost) 17:21:28 [mytest]> create table Orders  
        -> (order_id int unsigned not null auto_increment,
        -> product_name varchar(30) not null,
        -> price decimal(8,2) not null,
        -> amount smallint not null,
        -> primary key(order_id));
    Query OK, 0 rows affected (0.13 sec)  -- 创建Orders表
    
    (root@localhost) 17:26:40 [mytest]> insert into Orders values 
        -> (null, 'cpu', 135.5 ,1),
        -> (null, 'memory', 48.2, 3),
        -> (null, 'cpu', 125.6, 3),
        -> (null, 'cpu', 105.3, 4);
    Query OK, 4 rows affected (0.06 sec)  -- 插入测试数据
    Records: 4  Duplicates: 0  Warnings: 0
    
    (root@localhost) 17:26:42 [mytest]> select * from  Orders;
    +----------+--------------+--------+--------+
    | order_id | product_name | price  | amount |
    +----------+--------------+--------+--------+
    |        1 | cpu          | 135.50 |      1 |
    |        2 | memory       |  48.20 |      3 |
    |        3 | cpu          | 125.60 |      3 |
    |        4 | cpu          | 105.30 |      4 |
    +----------+--------------+--------+--------+
    4 rows in set (0.00 sec)
    
    -- 建立一个模拟物化视图的表(即用这张表来模拟物化视图)
    (root@localhost) 17:28:36 [mytest]> CREATE TABLE Orders_MV(
        -> product_name VARCHAR(30) NOT NULL
        -> , price_sum DECIMAL(8,2) NOT NULL
        -> , amount_sum INT NOT NULL
        -> , price_avg FLOAT NOT NULL
        -> , orders_cnt INT NOT NULL
        -> , UNIQUE INDEX (product_name)
        -> );
    Query OK, 0 rows affected (0.00 sec)
    --创建一个普通视图
    (root@localhost) 17:28:36 [mytest]> CREATE VIEW v_orders AS SELECT
        -> product_name,sum(price),sum(amount),avg(price),count(1)
        -> FROM Orders
        -> GROUP BY product_name;
    Query OK, 0 rows affected (0.04 sec)
    
    -- 通过Orders表的数据,将测试数据初始化到Orders_MV表中
    (root@localhost) 17:31:22 [mytest]> insert into Orders_MV
        -> select product_name, sum(price),sum(amount), avg(price), count(*)
        -> from Orders
        -> group by product_name;
    Query OK, 2 rows affected (0.00 sec)
    Records: 2  Duplicates: 0  Warnings: 0
    
    (root@localhost) 17:32:37 [mytest]>  select * from Orders_MV;
    +--------------+-----------+------------+-----------+------------+
    | product_name | price_sum | amount_sum | price_avg | orders_cnt |
    +--------------+-----------+------------+-----------+------------+
    | cpu          |    366.40 |          8 |   122.133 |          3 |
    | memory       |     48.20 |          3 |      48.2 |          1 |
    +--------------+-----------+------------+-----------+------------+
    2 rows in set (0.00 sec)
    
    -- 在MySQL workbench中输入,比较方便
    delimiter //
    
    CREATE TRIGGER tgr_Orders_insert -- 创建触发器为tgr_Orders_insert
    	AFTER INSERT ON Orders  -- 触发器是INSERT类型的,且作用于Orders表
    	FOR EACH ROW
    BEGIN
    	SET @old_price_sum := 0;  -- 设置临时存放Orders_MV表(模拟物化视图)的字段的变量
    	SET @old_amount_sum := 0;
    	SET @old_price_avg := 0;
    	SET @old_orders_cnt := 0;
    	SELECT   -- select ... into ... 在更新Orders_MV之前,将Orders_MV中对应某个产品的信息写入临时变量 
    		IFNULL(price_sum, 0),
    		IFNULL(amount_sum, 0),
    		IFNULL(price_avg, 0),
    		IFNULL(orders_cnt, 0)
    	FROM
    		Orders_MV
    	WHERE
    		product_name = NEW.product_name INTO @old_price_sum , @old_amount_sum , @old_price_avg , @old_orders_cnt;
    
    	SET @new_price_sum = @old_price_sum + NEW.price; -- 累加新的值
    	SET @new_amount_sum = @old_amount_sum + NEW.amount;
    	SET @new_orders_cnt = @old_orders_cnt + 1;
    	SET @new_price_avg = @new_price_sum / @new_orders_cnt ;
    	
        REPLACE INTO Orders_MV   
    			VALUES(NEW.product_name, @new_price_sum,
    				   @new_amount_sum, @new_price_avg, @new_orders_cnt );
       -- REPLACE 将对应的物品(唯一索引)的字段值替换new_xxx的值
    END;//
    
    delimiter ;
    
    (root@localhost) 17:37:35 [mytest]> insert into Orders values (null, 'ssd', 299, 3);
    Query OK, 1 row affected (0.01 sec)
    
    (root@localhost) 17:37:44 [mytest]> insert into Orders values (null, 'memory', 47.9, 5);
    Query OK, 1 row affected (0.00 sec)
    
    (root@localhost) 17:38:07 [mytest]> select * from Orders_MV;
    +--------------+-----------+------------+-----------+------------+
    | product_name | price_sum | amount_sum | price_avg | orders_cnt |
    +--------------+-----------+------------+-----------+------------+
    | cpu          |    366.40 |          8 |   122.133 |          3 |
    | memory       |     96.10 |          8 |     48.05 |          2 | -- 数量自动增加了1,价格也发生了变化
    | ssd          |    299.00 |          3 |       299 |          1 | -- 新增加的ssd产品
    +--------------+-----------+------------+-----------+------------+
    3 rows in set (0.00 sec)
    
    (root@localhost) 17:38:09 [mytest]> select * from v_orders;
    +--------------+------------+-------------+------------+----------+
    | product_name | sum(price) | sum(amount) | avg(price) | count(1) |
    +--------------+------------+-------------+------------+----------+
    | cpu          |     366.40 |           8 | 122.133333 |        3 |
    | memory       |      96.10 |           8 |  48.050000 |        2 |
    | ssd          |     299.00 |           3 | 299.000000 |        1 |
    +--------------+------------+-------------+------------+----------+
    3 rows in set (0.00 sec)
    
    --
    -- IFNULL MySQL内建函数的演示
    --
    (root@localhost) 08:47:45 [mytest]> select @test;
    +-------+
    | @test |
    +-------+
    | NULL  |  -- 当前会话中没有test变量
    +-------+
    1 row in set (0.00 sec)
    
    (root@localhost) 08:47:46 [mytest]>  select ifnull(@test, 100);   -- 如果test为NULL,则ifnull返回100
    +--------------------+
    | ifnull(@test, 100) |
    +--------------------+
    | 100                |  -- ifnull函数return的值是100
    +--------------------+
    1 row in set (0.00 sec)
    
    (root@localhost) 08:48:30 [mytest]> select @test;
    +-------+
    | @test |
    +-------+
    | NULL  |  -- 但是test还是NULL
    +-------+
    1 row in set (0.00 sec)
    
    (root@localhost) 08:48:40 [mytest]> set @test:=200;  -- 给test变量赋值为200
    Query OK, 0 rows affected (0.00 sec)
    
    (root@localhost) 08:48:49 [mytest]> select ifnull(@test, 100);  -- 再次ifnull判断,此时test不为null,则返回test变量的值
    +--------------------+
    | ifnull(@test, 100) |
    +--------------------+
    |                200 |  -- test不为null。返回test的值200
    +--------------------+
    1 row in set (0.00 sec)
    
    --
    -- select into 用法
    --
    (root@localhost) 08:51:19 [mytest]> select @id_1;
    +-------+
    | @id_1 |
    +-------+
    |  NULL | -- 当前变量id_01为null 
    +-------+
    1 row in set (0.00 sec)
    
    (root@localhost) 08:52:10 [mytest]> select @score_1;
    +----------+
    | @score_1 |
    +----------+
    |     NULL |  -- 当前变量score_01为null
    +----------+
    1 row in set (0.00 sec)
    
    (root@localhost) 08:53:56 [mytest]> select * from t_rank;
    +------+-------+------+
    | id   | score | c    |
    +------+-------+------+
    |    1 |    10 |    0 |
    |    2 |    20 |    0 |
    |    3 |    30 |    0 |
    |    4 |    30 |    0 |
    |    5 |    40 |    0 |
    |    6 |    40 |    0 |
    +------+-------+------+
    6 rows in set (0.00 sec)
    
    (root@localhost) 08:55:04 [mytest]> select id,score from t_rank where id =1 into @id_01,@score_01;-- 选择id=1的记录,将对应的id和score赋值给变量 id_01 和 score_01
    Query OK, 1 row affected (0.00 sec)
    
    (root@localhost) 08:55:27 [mytest]> select @id_01;
    +-------+
    | @id_1 |
    +-------+
    |     1 |
    +-------+
    1 row in set (0.00 sec)
    
    (root@localhost) 08:55:41 [mytest]> select @score_01;
    +-----------+
    | @score_01 |
    +-----------+
    |        10 |
    +-----------+
    1 row in set (0.00 sec)
    
    -- 触发器对性能会有影响,相当于在一个事物中插入了其他的事物
    
  • 相关阅读:
    真理
    使用C#调用QC的接口
    如何让asp.net应用程序定时自动执行代码
    对话
    科学●哲学●艺术●恶搞
    避免asp.net程序session过期的一个另类方法
    醉翁之意不在酒
    测试团队的新兴职位:测试设计师
    1和0的世界
    名词解释:高阻态,上拉电阻
  • 原文地址:https://www.cnblogs.com/gczheng/p/8011922.html
Copyright © 2020-2023  润新知