一、视图

(一)什么是视图

  1. 视图是一个虚拟表(非真实存在),其本质是【根据SQL语句获取动态的数据集,并为其命名】,用户使用时只需要使用【名称】即可获得结果集,可以将该结果当作表来使用。
  2. 使用视图我们可以把查询结果过程中的临时表摘出来,用视图去实现,这样可以以后再想操作该临时表的数据时就无需重写复杂的sql了,直接去视图中查找即可,但视图有明显的效率问题,并且视图是存放在数据库中的,如果我们程序中使用的sql过分依赖数据库中的视图,即强耦合,那就意味着扩展sql极为不便,因此并不推荐使用。
  3. 示例:临时表应用
  1. # 两张有关系的表
  2. mysql> select * from course;
  3. +-----+--------+------------+
  4. | cid | cname | teacher_id |
  5. +-----+--------+------------+
  6. | 1 | 生物 | 1 |
  7. | 2 | 物理 | 2 |
  8. | 3 | 体育 | 3 |
  9. | 4 | 美术 | 2 |
  10. +-----+--------+------------+
  11. 4 rows in set (0.00 sec)
  12. mysql> select * from teacher;
  13. +-----+-----------------+
  14. | tid | tname |
  15. +-----+-----------------+
  16. | 1 | 张磊老师 |
  17. | 2 | 李平老师 |
  18. | 3 | 刘海燕老师 |
  19. | 4 | 朱云海老师 |
  20. | 5 | 李杰老师 |
  21. +-----+-----------------+
  22. 5 rows in set (0.00 sec)
  23. # 查询李平老师教授的课程名
  24. mysql> select cname from course where teacher_id = (select tid from teacher where tname='李平老师');
  25. +--------+
  26. | cname |
  27. +--------+
  28. | 物理 |
  29. | 美术 |
  30. +--------+
  31. 2 rows in set (0.00 sec)
  32. # 子查询出临时表,作为teacher_id等判断依据
  33. select tid from teacher where tname='李平老师'

(二)创建视图

  1. **语法:CREATE VIEW 视图名称 AS SQL语句;**
  1. # 于是查询李平老师教授的课程名的sql可以改写为
  2. mysql> select cname from course where teacher_id = (select tid from teacher_view);
  3. +--------+
  4. | cname |
  5. +--------+
  6. | 物理 |
  7. | 美术 |
  8. +--------+
  1. **注意:**
  2. 1.使用视图后就无需每次都重写子查询的sql,但是这么效率不高,还不如我们写子查询效率高;
  3. 2.而且有一个致命的问题:视图是存放到数据库里的,如果我们程序中的SQL过分依赖于数据库中存放的视图,那么意味着,一旦SQL需要修改且涉及到视图的部分,则必须去数据库中进行修改,而通常在公司中数据库有专门的DBA负责,修改需付出巨大的沟通成本,极不方便。

(三)使用视图

  1. # 修改原始表,原始视图也跟着改
  2. mysql> select * from course;
  3. +-----+--------+------------+
  4. | cid | cname | teacher_id |
  5. +-----+--------+------------+
  6. | 1 | 生物 | 1 |
  7. | 2 | 物理 | 2 |
  8. | 3 | 体育 | 3 |
  9. | 4 | 美术 | 2 |
  10. +-----+--------+------------+
  11. # 创建表course的视图
  12. mysql> create view course_view as select * from course;
  13. mysql> select * from course_view;
  14. +-----+--------+------------+
  15. | cid | cname | teacher_id |
  16. +-----+--------+------------+
  17. | 1 | 生物 | 1 |
  18. | 2 | 物理 | 2 |
  19. | 3 | 体育 | 3 |
  20. | 4 | 美术 | 2 |
  21. +-----+--------+------------+
  22. # 更新视图中的数据
  23. mysql> update course_view set cname='xxx';
  24. # 往视图中插入数据
  25. mysql> insert into course_view values(5,'yyy',2);
  26. # 发现原始表的记录也跟着修改了
  27. mysql> select * from course;
  28. +-----+-------+------------+
  29. | cid | cname | teacher_id |
  30. +-----+-------+------------+
  31. | 1 | xxx | 1 |
  32. | 2 | xxx | 2 |
  33. | 3 | xxx | 3 |
  34. | 4 | xxx | 2 |
  35. | 5 | yyy | 2 |
  36. +-----+-------+------------+
  1. 我们不应该修改视图中的记录,而且在涉及多个表的情况下是根本无法修改视图中的记录的,如下图

视图、触发器、事务、存储过程、函数 - 图1

(四)修改视图

  1. **语法:ALTER VIEW 视图名称 AS SQL 语句;**
  1. mysql> alter view teacher_view as select * from course where cid>3;
  2. mysql> select * from teacher_view;
  3. +-----+-------+------------+
  4. | cid | cname | teacher_id |
  5. +-----+-------+------------+
  6. | 4 | xxx | 2 |
  7. | 5 | yyy | 2 |
  8. +-----+-------+------------+

(五)删除视图

  1. **语法:DROP VIEW 视图名称;**
  1. DROP VIEW teacher_view;

二、触发器

(一)为何要使用触发器

  1. 使用触发器可以定制用户对表进行【增、删、改】操作前后的行为。!!注意:没有修改。

(二)创建触发器

  1. # 创建触发器的语法:
  2. # 插入前
  3. CREATE TRIGGER tri_before_insert_tb1 BEFORE INSERT ON tb1 FOR EACH ROW
  4. BEGIN
  5. ...
  6. END
  7. # 插入后
  8. CREATE TRIGGER tri_after_insert_tb1 AFTER INSERT ON tb1 FOR EACH ROW
  9. BEGIN
  10. ...
  11. END
  12. # 删除前
  13. CREATE TRIGGER tri_before_delete_tb1 BEFORE DELETE ON tb1 FOR EACH ROW
  14. BEGIN
  15. ...
  16. END
  17. # 删除后
  18. CREATE TRIGGER tri_after_delete_tb1 AFTER DELETE ON tb1 FOR EACH ROW
  19. BEGIN
  20. ...
  21. END
  22. # 更新前
  23. CREATE TRIGGER tri_before_update_tb1 BEFORE UPDATE ON tb1 FOR EACH ROW
  24. BEGIN
  25. ...
  26. END
  27. # 更新后
  28. CREATE TRIGGER tri_after_update_tb1 AFTER UPDATE ON tb1 FOR EACH ROW
  29. BEGIN
  30. ...
  31. END
  1. 示例:插入后触发触发器
  2. **特别的语法:NEW表示即将插入的数据行,OLD表示即将删除的数据行。**
  1. # 准备表
  2. CREATE TABLE cmd (
  3. id INT PRIMARY KEY auto_increment,
  4. USER CHAR (32),
  5. priv CHAR (10),
  6. cmd CHAR (64),
  7. sub_time datetime, # 提交时间
  8. success enum ('yes', 'no') # 0代表执行失败
  9. );
  10. CREATE TABLE errlog (
  11. id INT PRIMARY KEY auto_increment,
  12. err_cmd CHAR (64),
  13. err_time datetime
  14. );
  15. # 创建触发器
  16. mysql> delimiter // # 将结束符号修改为//,让MySQL允许我们输入多个含分号";"的语句,最后修改回
  17. 来,让前面的争端语句生效。
  18. mysql> CREATE TRIGGER tri_after_insert_cmd AFTER INSERT ON cmd FOR EACH ROW
  19. -> BEGIN
  20. -> IF NEW.success = 'no' THEN # 等值判断只有一个等号
  21. -> INSERT INTO errlog(err_cmd, err_time) VALUES(NEW.cmd, NEW.sub_time) ; # 必须加分号
  22. -> END IF ; # 必须加分号
  23. -> END//
  24. Query OK, 0 rows affected (0.10 sec)
  25. mysql> delimiter ;
  26. mysql>
  27. # 往表cmd中插入记录,触发触发器,根据IF的条件决定是否插入错误日志
  28. INSERT INTO cmd (
  29. USER,
  30. priv,
  31. cmd,
  32. sub_time,
  33. success
  34. )
  35. VALUES
  36. ('egon','0755','ls -l /etc',NOW(),'yes'),
  37. ('egon','0755','cat /etc/passwd',NOW(),'no'),
  38. ('egon','0755','useradd xxx',NOW(),'no'),
  39. ('egon','0755','ps aux',NOW(),'yes');
  40. # 查询错误日志,发现有两条
  41. mysql> select * from errlog;
  42. +----+-----------------+---------------------+
  43. | id | err_cmd | err_time |
  44. +----+-----------------+---------------------+
  45. | 1 | cat /etc/passwd | 2020-09-09 16:50:55 |
  46. | 2 | useradd xxx | 2020-09-09 16:50:55 |
  47. +----+-----------------+---------------------+
  48. 2 rows in set (0.00 sec)

(三)使用触发器

  1. 触发器无法由用户直接调用,只是由于对表的【增/删/改】操作被动引发。

(四)删除触发器

  1. DROP TRIGGER tri_after_insert_cmd;

三、事务

(一)什么是事务

  1. 数据库事务是指作为单个逻辑单元执行的一系列操作(SQL语句)。这些操作要么全部执行,要么全部不执行。

(二)为什么需要事务

  1. _经典的银行转账行为,A账户转给B账户10元,数据库操作需要两步,第一步A账户减10元,第二步B账户加10元,如果没有事务并且在两步中间发生异常,就会导致A的账户少了10元,但B的账户没有变化,如果不能保证这两步操作统一,银行的转账业务也没法进行展开了。_
  2. 事务管理是每个数据库(OracleMySQLdb等)都必须实现的。

(三)事务特性(4种):

  1. 1.原子性(atomicty):强调事务的不可分割;
  2. 2.一致性(consistency):事务的执行的前后数据的完整性保持一致;
  3. 3.隔离性(isolation):一个事务执行的过程中,不应该受到其他事务的干扰;
  4. 4.持久性(durability):事务一旦结束,数据就持久到数据库。

(四)事务运行模式(3种):

  1. 1.自动提交事务:默认的事务管理模式。如果一个语句成功地完成,则提交该语句;如果遇到错误,则回滚该语句。
  2. 2.显式事务:以 BEGIN TRANSACTION 显式开始,以 COMMIT ROLLBACK 显式结束。
  3. 3.隐式事务:当连接以此模式进行操作时,sql将在提交或回滚当前事务后自动启动新事务。无须描述事务的开始,只需提交或回滚每个事务。它生成连续的事务链。

(五)使用案例

1.在MySQL中的使用

  1. # 示例:
  2. create table user(
  3. id int primary key auto_increment,
  4. name char(32),
  5. balance int
  6. );
  7. insert into user(name,balance) values
  8. ('wsb',1000),
  9. ('egg',1000),
  10. ('ysb',1000);
  11. # 原子操作
  12. start transaction;
  13. update user set balance=900 where name='wsb'; # 买支付100元
  14. update user set balance=1010 where name='egg'; # 中介拿走10元
  15. update user set balance=1090 where name='ysb'; # 卖家拿到90元
  16. commit; # 真正提交到数据库中
  17. # 出现异常,回滚到初始状态
  18. start transaction;
  19. update user set balance=900 where name='wsb'; # 买支付100元
  20. update user set balance=1010 where name='egg'; # 中介拿走10元
  21. update user set balance=1090 where name='ysb'; # 卖家拿到90元,出现异常,没有拿到
  22. rollback;
  23. commit;
  24. mysql> select * from user;
  25. +----+------+---------+
  26. | id | name | balance |
  27. +----+------+---------+
  28. | 1 | wsb | 1000 |
  29. | 2 | egg | 1000 |
  30. | 3 | ysb | 1000 |
  31. +----+------+---------+
  32. 3 rows in set (0.00 sec)

2.在pymysql中实现事务处理

  1. **pymysql执行的所有sql语句,都会默认放入一个事务中去。针对查询语句,事务并没有什么影响,更多的是针对修改语句。**
  1. import pymysql # pip3 install pymysql
  2. connect = pymysql.connect(host="127.0.0.1", port=3306, user="root", password="123", db="db13", charset="utf8mb4")
  3. cursor = connect.cursor(cursor=pymysql.cursors.DictCursor)
  4. try:
  5. cursor.execute(sql_1)
  6. cursor.execute(sql_2)
  7. cursor.execute(sql_3)
  8. except Exception as e:
  9. connect.rollback() # 事务回滚
  10. print('事务处理失败', e)
  11. else:
  12. connect.commit() # 事务提交
  13. print('事务处理成功', cursor.rowcount) # 关闭连接
  14. connect.close()

(六)总结

  1. 事务用于将某些操作的多个SQL语句作为原子性操作,一旦有某一个错误,即可回滚到原来的状态,从而保证数据的完整性。

四、存储过程

(一)什么是存储过程

  1. 存储过程包含了一系列可执行的sql语句,存储过程存放于MySQL中,通过调用它的名字可以执行其内部的一堆sql语句。相当于一个封装好的接口,里面有各种业务逻辑。

1.补充:程序于数据库结合使用的三种方式

  1. # 方式一:
  2. MySQL:存储过程
  3. 程序:调用存储过程
  4. # 方式二:
  5. MySQL:建库、表及关系
  6. 程序:纯SQL语句(开发人员写)
  7. # 方式三:
  8. MySQL:建库、表及关系
  9. 程序:类和对象,即ORM(本质还是纯SQL语句)(对象关系映射;类映射成表,对象映射成表的记录)

(二)存储过程的优缺点

  1. 使用存储过程的优点:
  1. # 1) 基于替代程序写的SQL语句,实现程序于SQL解耦;
  2. # 2) 基于网络传输,传别名的数据量小,而直接传SQL数据量大。
  1. 使用存储过程的缺点:
  1. # 1) 程序扩展功能不方便

(三)创建简单存储过程(无参)

  1. # 创建无参存储过程
  2. mysql> delimiter $$
  3. mysql> create procedure p1()
  4. -> begin
  5. -> select * from emp;
  6. -> end $$
  7. Query OK, 0 rows affected (0.00 sec)
  8. mysql>
  9. mysql> delimiter ;
  10. mysql>
  11. # 在MySQL中调用:
  12. mysql> call p1();
  13. +----+------------+--------+------+--------+
  14. | id | name | sex | age | dep_id |
  15. +----+------------+--------+------+--------+
  16. | 1 | egon | male | 18 | 200 |
  17. | 2 | alex | female | 48 | 201 |
  18. | 3 | wupeiqi | male | 38 | 201 |
  19. | 4 | yuanhao | female | 28 | 202 |
  20. | 5 | liwenzhou | male | 18 | 200 |
  21. | 6 | jingliyang | female | 18 | 204 |
  22. | 7 | xxx | male | 66 | NULL |
  23. +----+------------+--------+------+--------+
  24. # 在python中基于pymysql调用:
  25. cursor.callproc('p1')
  26. print(cursor.fetchall())

(四)创建存储过程(有参)

  1. 对于存储过程,可以接收参数,其参数有三类:
  2. (1) in ————>仅用于接收传入参数用;
  3. (2) out ————>仅用于返回值用;
  4. (3) inout ————>既可以传入又可以当作返回值。
  1. # 创建有参存储过程
  2. delimiter $$
  3. create procedure p2(
  4. in n int, # 指定多个字段,必须加逗号隔开,最后一个不加逗号
  5. out res int
  6. )
  7. begin
  8. select * from emp where id > n; # 查找id>n 的记录
  9. set res=1; # 自己设定,控制返回值,代表本条成功与否
  10. end $$
  11. delimiter ;
  12. # 在MySQL中调用
  13. mysql> set @x=1111; # 必须事先定义一个变量,后面要查看变量的结果
  14. Query OK, 0 rows affected (0.00 sec)
  15. mysql> call p2(3,x);
  16. ERROR 1414 (42000): OUT or INOUT argument 2 for routine d47.p2 is not a variable or NEW pseudo-variable in BEFORE trigger
  17. mysql> call p2(3,@x);
  18. +----+------------+--------+------+--------+
  19. | id | name | sex | age | dep_id |
  20. +----+------------+--------+------+--------+
  21. | 4 | yuanhao | female | 28 | 202 |
  22. | 5 | liwenzhou | male | 18 | 200 |
  23. | 6 | jingliyang | female | 18 | 204 |
  24. | 7 | xxx | male | 66 | NULL |
  25. +----+------------+--------+------+--------+
  26. 4 rows in set (0.00 sec)
  27. mysql> select @3;
  28. +------+
  29. | @3 |
  30. +------+
  31. | NULL |
  32. +------+
  33. 1 row in set (0.00 sec)
  34. mysql> select @x;
  35. +------+
  36. | @x |
  37. +------+
  38. | 1 |
  39. +------+
  40. 1 row in set (0.00 sec)
  1. # 在python中基于pyMySQL调用
  2. import pymysql # pip3 install pymysql
  3. connect = pymysql.connect(host="127.0.0.1", port=3306, user="root", password="123", db="day47", charset="utf8mb4")
  4. cursor = connect.cursor(cursor=pymysql.cursors.DictCursor)
  5. cursor.callproc('p2', (3, 0)) # @_p2_0=3,@_p2_1=0 # 0相当于set @res=0
  6. '''
  7. set @_p2_0=3 【底层组织成的一个变量的格式】
  8. set @_p2_1=0
  9. call p2(@_p2_0,@_p2_1); #@_p2_0代表第一个参数,@_p2_1代表第二个参数,即返回值
  10. '''
  11. print(cursor.fetchall()) # 查询select的查询结果
  12. # [{'id': 4, 'name': 'yuanhao', 'sex': 'female', 'age': 28, 'dep_id': 202}, {'id': 5, 'name': 'liwenzhou', 'sex': 'male', 'age': 18, 'dep_id': 200}, {'id': 6, 'name': 'jingliyang', 'sex': 'female', 'age': 18, 'dep_id': 204}, {'id': 7, 'name': 'xxx', 'sex': 'male', 'age': 66, 'dep_id': None}]
  13. cursor.execute('select @_p2_0,') # 执行一个sql语句查看返回值
  14. print(cursor.fetchall()) # [{'@_p2_0': 3}] 【3这个值不会变,一直是3】
  15. cursor.execute('select @_p2_1')
  16. print(cursor.fetchall()) # [{'@_p2_1': 1}] 【会变,查询成功返回值是1,这个是我们自己在存储过程中设定的那个值】
  17. cursor.close()
  18. connect.close()

(五)执行存储过程

1.在MySQL中执行存储过程

  1. -- 无参数
  2. call proc_name()
  3. -- 有参数,全in
  4. call proc_name(1,2)
  5. -- 有参数,有inout, inout
  6. set @t1=0;
  7. set @t2=3;
  8. call proc_name(1,2,@t1,@t2)

2.在python中基于pymysql执行存储过程

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. import pymysql
  4. conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
  5. cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
  6. # 执行存储过程
  7. cursor.callproc('p1', args=(1, 22, 3, 4))
  8. # 获取执行完存储的参数
  9. cursor.execute("select @_p1_0,@_p1_1,@_p1_2,@_p1_3")
  10. result = cursor.fetchall()
  11. conn.commit()
  12. cursor.close()
  13. conn.close()
  14. print(result)

(六)删除存储过程

  1. DROP procedure prco_name;

五、函数

(一)内置函数

1.需要掌握函数:date_format

  1. # 1) 基本使用
  2. mysql> SELECT DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y');
  3. -> 'Sunday October 2009'
  4. mysql> SELECT DATE_FORMAT('2007-10-04 22:23:00', '%H:%i:%s');
  5. -> '22:23:00'
  6. mysql> SELECT DATE_FORMAT('1900-10-04 22:23:00',
  7. -> '%D %y %a %d %m %b %j');
  8. -> '4th 00 Thu 04 10 Oct 277'
  9. mysql> SELECT DATE_FORMAT('1997-10-04 22:23:00',
  10. -> '%H %k %I %r %T %S %w');
  11. -> '22 22 10 10:23:00 PM 22:23:00 00 6'
  12. mysql> SELECT DATE_FORMAT('1999-01-01', '%X %V');
  13. -> '1998 52'
  14. mysql> SELECT DATE_FORMAT('2006-06-00', '%d');
  15. -> '00'
  1. # 2) 代码示例 :用于按月份进行分组
  2. mysql> CREATE TABLE blog (
  3. -> id INT PRIMARY KEY auto_increment,
  4. -> NAME CHAR (32),
  5. -> sub_time datetime
  6. -> );
  7. mysql> INSERT INTO blog (NAME, sub_time)
  8. -> VALUES
  9. -> ('第1篇','2015-03-01 11:31:21'),
  10. -> ('第2篇','2015-03-11 16:31:21'),
  11. -> ('第3篇','2016-07-01 10:21:31'),
  12. -> ('第4篇','2016-07-22 09:23:21'),
  13. -> ('第5篇','2016-07-23 10:11:11'),
  14. -> ('第6篇','2016-07-25 11:21:31'),
  15. -> ('第7篇','2017-03-01 15:33:21'),
  16. -> ('第8篇','2017-03-01 17:32:21'),
  17. -> ('第9篇','2017-03-01 18:31:21');
  18. mysql> select date_format(sub_time,"%Y-%m") as t,count(id) from blog group by t;
  19. +---------+-----------+
  20. | t | count(id) |
  21. +---------+-----------+
  22. | 2015-03 | 2 |
  23. | 2016-07 | 4 |
  24. | 2017-03 | 3 |
  25. +---------+-----------+
  26. 3 rows in set (0.00 sec)

2.其他内置函数

http://doc.mysql.cn/mysql5/refman-5.1-zh.html-chapter/functions.html#encryption-functions

(二)自定义函数

1.注意事项:

(1)函数中不要写SQL语句(否则会报错),函数仅仅是一个功能,是一个在SQL中被应用的功能;

(2)若想在begin···end···中写SQL,需要使用存储过程。

  1. delimiter //
  2. create function f1(
  3. i1 int,
  4. i2 int)
  5. returns int
  6. BEGIN
  7. declare num int;
  8. set num = i1 + i2;
  9. return(num);
  10. END //
  11. delimiter ;
  1. delimiter //
  2. create function f5(
  3. i int
  4. )
  5. returns int
  6. begin
  7. declare res int default 0;
  8. if i = 10 then
  9. set res=100;
  10. elseif i = 20 then
  11. set res=200;
  12. elseif i = 30 then
  13. set res=300;
  14. else
  15. set res=400;
  16. end if;
  17. return res;
  18. end //
  19. delimiter ;

(三)删除函数

  1. DROP function func_name;

(四)执行函数

  1. # 获取返回值
  2. select UPPER('egon') into @res;
  3. SELECT @res;
  4. # 在查询中使用
  5. select f1(11,nid),name from tb2;

六、流程控制

(一)if条件语句

  1. delimiter //
  2. CREATE PROCEDURE proc_if ()
  3. BEGIN
  4. declare i int default 0;
  5. if i = 1 THEN
  6. SELECT 1;
  7. ELSEIF i = 2 THEN
  8. SELECT 2;
  9. ELSE
  10. SELECT 7;
  11. END IF;
  12. END //
  13. delimiter ;

(二)循环语句

1.while循环

  1. delimiter //
  2. CREATE PROCEDURE proc_while ()
  3. BEGIN
  4. DECLARE num INT ;
  5. SET num = 0 ;
  6. WHILE num < 10 DO
  7. SELECT
  8. num ;
  9. SET num = num + 1 ;
  10. END WHILE ;
  11. END //
  12. delimiter ;

2.repeat循环

  1. delimiter //
  2. CREATE PROCEDURE proc_repeat ()
  3. BEGIN
  4. DECLARE i INT ;
  5. SET i = 0 ;
  6. repeat
  7. select i;
  8. set i = i + 1;
  9. until i >= 5
  10. end repeat;
  11. END //
  12. delimiter ;

3.loop

  1. BEGIN
  2. declare i int default 0;
  3. loop_label: loop
  4. set i=i+1;
  5. if i<8 then
  6. iterate loop_label;
  7. end if;
  8. if i>=10 then
  9. leave loop_label;
  10. end if;
  11. select i;
  12. end loop loop_label;
  13. END