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)

-- 触发器对性能会有影响,相当于在一个事物中插入了其他的事物
原文地址:https://www.cnblogs.com/gczheng/p/8011922.html