数据库开发——MySQL——内置功能
一、视图
视图本质上是根据SQL语句获取的动态数据集并命名存储在内存中的虚拟表。
用户使用时只需使用视图的名称即可获取其数据集并当做表来使用。
使用视图我们可以吧查询过程中的临时表存储为视图,这样以后该再想操作该临时数据表时就无需重写复杂的SQL了,直接去视图里查找即可。
但视图有明显地效率问题,并且视图是存放在数据库中的,如果程序中使用的SQL过分依赖数据库中的视图,即强耦合,那就意味着扩展SQL极为不便。
1.创建视图
语法:CREATE VIEW 视图名称 AS SQL语句
mysql> create view 201_view as select * from employee where dep_id = 201;
Query OK, 0 rows affected (0.31 sec)
mysql> select * from 201_view;
+----+---------+--------+------+--------+
| id | name | sex | age | dep_id |
+----+---------+--------+------+--------+
| 3 | wupeiqi | male | 38 | 201 |
| 8 | Coco | female | 19 | 201 |
+----+---------+--------+------+--------+
2 rows in set (0.00 sec)
注意:
-
使用视图以后就无需每次都重写子查询的sql,但是这么效率并不高,还不如我们写子查询的效率高。
-
视图是存放到数据库里的,如果程序中的sql过分依赖于数据库中存放的视图,一旦sql需要修改且涉及到视图的部分,则必须去数据库中进行修改,而通常在公司中数据库有专门的DBA负责,极其地不方便
2.修改视图
修改视图会导致原始表中的数据也跟着改变。
mysql> update 201_view set dep_id = 200 where name = "Coco";
Query OK, 2 rows affected (0.21 sec)
Rows matched: 2 Changed: 2 Warnings: 0
mysql> select * from 201_view; # 不符合视图条件的会直接被删掉
+----+---------+------+------+--------+
| id | name | sex | age | dep_id |
+----+---------+------+------+--------+
| 3 | wupeiqi | male | 38 | 201 |
+----+---------+------+------+--------+
1 row in set (0.00 sec)
mysql> select * from employee; # 原始表中的数据被修改
+----+------------+--------+------+--------+
| id | name | sex | age | dep_id |
+----+------------+--------+------+--------+
| 1 | egon | male | 18 | 200 |
| 3 | wupeiqi | male | 38 | 201 |
| 4 | yuanhao | female | 28 | 202 |
| 5 | liwenzhou | male | 18 | 200 |
| 6 | jingliyang | female | 18 | 204 |
| 8 | Coco | female | 19 | 200 |
| 9 | Bei | female | 2 | 202 |
| 11 | Coco | female | 19 | 200 |
| 12 | Bei | female | 2 | 202 |
+----+------------+--------+------+--------+
9 rows in set (0.00 sec)
3.修改视图
语法:ALTER VIEW 视图名称 AS SQL语句
**mysql> alter view 201_view as select * from employee where dep_id = 200;
Query OK, 0 rows affected (0.46 sec)
mysql> select * from 201_view;
+----+-----------+--------+------+--------+
| id | name | sex | age | dep_id |
+----+-----------+--------+------+--------+
| 1 | egon | male | 18 | 200 |
| 5 | liwenzhou | male | 18 | 200 |
| 8 | Coco | female | 19 | 200 |
| 11 | Coco | female | 19 | 200 |
+----+-----------+--------+------+--------+
4 rows in set (0.00 sec)**
4.删除视图
语法:DROP VIEW 视图名称
二、触发器
使用触发器可以定制用户对表进行【增、删、改】操作时前后的行为,注意:没有查询。
1.创建触发器
插入触发器
# 插入前
CREATE TRIGGER tri_before_insert_tb1 BEFORE INSERT ON tb1 FOR EACH ROW
BEGIN
...
END
# 插入后
CREATE TRIGGER tri_after_insert_tb1 AFTER INSERT ON tb1 FOR EACH ROW
BEGIN
...
END
删除触发器
# 删除前
CREATE TRIGGER tri_before_delete_tb1 BEFORE DELETE ON tb1 FOR EACH ROW
BEGIN
...
END
# 删除后
CREATE TRIGGER tri_after_delete_tb1 AFTER DELETE ON tb1 FOR EACH ROW
BEGIN
...
END
更新触发器
# 更新前
CREATE TRIGGER tri_before_update_tb1 BEFORE UPDATE ON tb1 FOR EACH ROW
BEGIN
...
END
# 更新后
CREATE TRIGGER tri_after_update_tb1 AFTER UPDATE ON tb1 FOR EACH ROW
BEGIN
...
END
测试
创建命令表:
mysql> CREATE TABLE cmd (
-> id INT PRIMARY KEY auto_increment,
-> USER CHAR (32),
-> priv CHAR (10),
-> cmd CHAR (64),
-> sub_time datetime, #提交时间
-> success enum ('yes', 'no') #0代表执行失败
-> );
Query OK, 0 rows affected (1.58 sec)
创建错误日志表:
mysql> CREATE TABLE errlog (
-> id INT PRIMARY KEY auto_increment,
-> err_cmd CHAR (64),
-> err_time datetime
-> );
Query OK, 0 rows affected (0.66 sec)
NEW表示即将插入的数据行,OLD表示即将删除的数据行。
创建触发器:
mysql> delimiter //
mysql> CREATE TRIGGER tri_after_insert_cmd AFTER INSERT ON cmd FOR EACH ROW
-> BEGIN
-> IF NEW.success = 'no' THEN #等值判断只有一个等号
-> INSERT INTO errlog(err_cmd, err_time) VALUES(NEW.cmd, NEW.sub_time) ; #必须加分号
-> END IF ; #必须加分号
-> END//
Query OK, 0 rows affected (0.32 sec)
mysql> delimiter ;
插入数据:
mysql> INSERT INTO cmd (
-> USER,
-> priv,
-> cmd,
-> sub_time,
-> success
-> )
-> VALUES
-> ('Alex','0755','ls -l /etc',NOW(),'yes'),
-> ('Alex','0755','cat /etc/passwd',NOW(),'no'),
-> ('Alex','0755','useradd xxx',NOW(),'no'),
-> ('Alex','0755','ps aux',NOW(),'yes');
Query OK, 4 rows affected (0.71 sec)
Records: 4 Duplicates: 0 Warnings: 0
查询错误日志:
mysql> select * from errlog;
+----+-----------------+---------------------+
| id | err_cmd | err_time |
+----+-----------------+---------------------+
| 1 | cat /etc/passwd | 2020-02-17 17:37:38 |
| 2 | useradd xxx | 2020-02-17 17:37:38 |
+----+-----------------+---------------------+
2 rows in set (0.06 sec)
2.删除触发器
drop trigger tri_after_insert_cmd;
三、事务
事务用于将某些操作的多个SQL作为原子性操作,一旦有某一个出现错误,即可回滚到原来的状态,从而保证数据库数据完整性。
创建数据和表:
mysql> create table user(
-> id int primary key auto_increment,
-> name char(32),
-> balance int
-> );
Query OK, 0 rows affected (1.09 sec)
mysql> insert into user(name,balance)
-> values
-> ('wsb',1000),
-> ('egon',1000),
-> ('ysb',1000);
Query OK, 3 rows affected (0.14 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from user;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | wsb | 1000 |
| 2 | egon | 1000 |
| 3 | ysb | 1000 |
+----+------+---------+
3 rows in set (0.03 sec)
正常情况:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> update user set balance=900 where name='wsb'; #买支付100元
Query OK, 1 row affected (0.12 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> update user set balance=1010 where name='egon'; #中介拿走10元
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> update user set balance=1090 where name='ysb'; #卖家拿到90元
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> commit;
Query OK, 0 rows affected (0.15 sec)
mysql> select * from user;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | wsb | 900 |
| 2 | egon | 1010 |
| 3 | ysb | 1090 |
+----+------+---------+
3 rows in set (0.00 sec)
出现错误的情况:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> update user set balance=900 where name='wsb'; #买支付100元
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> update user set balance=1010 where name='egon'; #中介拿走10元
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> uppdate user set balance=1090 where name='ysb'; #卖家拿到90元,出现异常没有拿到
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'uppdate user set balance=1090 where name='ysb'' at line 1
mysql> rollback;
Query OK, 0 rows affected (0.04 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from user;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | wsb | 1000 |
| 2 | egon | 1000 |
| 3 | ysb | 1000 |
+----+------+---------+
3 rows in set (0.00 sec)
实现原理
delimiter //
create PROCEDURE p5(
OUT p_return_code tinyint
)
BEGIN
DECLARE exit handler for sqlexception
BEGIN
-- ERROR
set p_return_code = 1;
rollback;
END;
DECLARE exit handler for sqlwarning
BEGIN
-- WARNING
set p_return_code = 2;
rollback;
END;
START TRANSACTION;
DELETE from tb1; #执行失败
insert into blog(name,sub_time) values('yyy',now());
COMMIT;
-- SUCCESS
set p_return_code = 0; #0代表执行成功
END //
delimiter ;
四、存储过程
存储过程包含了一系列可执行的sql语句,存储过程存放于MySQL中,通过调用它的名字可以执行其内部的一堆sql。
使用存储过程的优点:
1.用于替代程序写的SQL语句,实现程序与sql解耦
2.基于网络传输,传别名的数据量小,而直接传sql数据量大
使用存储过程的缺点:
1. 程序员扩展功能不方便
1.无参的存储过程
mysql> delimiter //
mysql> create procedure p1()
-> begin
-> select * from employee;
-> insert into employee(name, sex, age, dep_id) values ("Alex", "male", 19, 200);
-> end //
Query OK, 0 rows affected (0.42 sec)
mysql> delimiter ;
mysql> show create procedure p1;
+-----------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------+----------------------+--------------------+
| Procedure | sql_mode | Create Procedure | character_set_client | collation_connection | Database Collation |
+-----------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------+----------------------+--------------------+
| p1 | ONLY_FULL_GROUP_BY | CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`()
begin
select * from employee;
insert into employee(name, sex, age, dep_id) values ("Alex", "male", 19, 200);
end | utf8 | utf8_general_ci | utf8_general_ci |
+-----------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------+----------------------+--------------------+
1 row in set (0.11 sec)
在MySQL中调用:
mysql> call p1();
+----+------------+--------+------+--------+
| id | name | sex | age | dep_id |
+----+------------+--------+------+--------+
| 1 | egon | male | 18 | 200 |
| 3 | wupeiqi | male | 38 | 201 |
| 4 | yuanhao | female | 28 | 202 |
| 5 | liwenzhou | male | 18 | 200 |
| 6 | jingliyang | female | 18 | 204 |
| 8 | Coco | female | 19 | 200 |
| 9 | Bei | female | 2 | 202 |
| 11 | Coco | female | 19 | 200 |
| 12 | Bei | female | 2 | 202 |
+----+------------+--------+------+--------+
9 rows in set (0.11 sec)
Query OK, 1 row affected (0.23 sec)
mysql> select * from employee;
+----+------------+--------+------+--------+
| id | name | sex | age | dep_id |
+----+------------+--------+------+--------+
| 1 | egon | male | 18 | 200 |
| 3 | wupeiqi | male | 38 | 201 |
| 4 | yuanhao | female | 28 | 202 |
| 5 | liwenzhou | male | 18 | 200 |
| 6 | jingliyang | female | 18 | 204 |
| 8 | Coco | female | 19 | 200 |
| 9 | Bei | female | 2 | 202 |
| 11 | Coco | female | 19 | 200 |
| 12 | Bei | female | 2 | 202 |
| 13 | Alex | male | 19 | 200 |
+----+------------+--------+------+--------+
10 rows in set (0.00 sec)
在python中调用:
import pymysql
conn = pymysql.connect(host="localhost", user="root", password="20001001", database="db")
cursor = conn.cursor()
cursor.callproc("p1")
print(cursor.fetchall())
输出结果为:
((1, 'egon', 'male', 18, 200), (3, 'wupeiqi', 'male', 38, 201), (4, 'yuanhao', 'female', 28, 202), (5, 'liwenzhou', 'male', 18, 200), (6, 'jingliyang', 'female', 18, 204), (8, 'Coco', 'female', 19, 200), (9, 'Bei', 'female', 2, 202), (11, 'Coco', 'female', 19, 200), (12, 'Bei', 'female', 2, 202), (13, 'Alex', 'male', 19, 200))
2.有参的存储过程
对于存储过程,可以接收参数,其参数有三类:
in 仅用于传入参数用
out 仅用于返回值用
inout 既可以传入又可以当作返回值
mysql> delimiter //
mysql> create procedure p2(inout n1 int)
-> begin
-> select * from employee where id > n1;
-> set n1 = 1;
-> end //
Query OK, 0 rows affected (0.42 sec)
mysql> delimiter ;
在MySQL中调用:
mysql> set @x = 3;
Query OK, 0 rows affected (0.00 sec)
mysql> call p2(@x);
+----+------------+--------+------+--------+
| id | name | sex | age | dep_id |
+----+------------+--------+------+--------+
| 4 | yuanhao | female | 28 | 202 |
| 5 | liwenzhou | male | 18 | 200 |
| 6 | jingliyang | female | 18 | 204 |
| 8 | Coco | female | 19 | 200 |
| 9 | Bei | female | 2 | 202 |
| 11 | Coco | female | 19 | 200 |
| 12 | Bei | female | 2 | 202 |
| 13 | Alex | male | 19 | 200 |
+----+------------+--------+------+--------+
8 rows in set (0.01 sec)
Query OK, 0 rows affected (0.10 sec)
mysql> select @x;
+------+
| @x |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
在python中调用:
import pymysql
conn = pymysql.connect(host="localhost", user="root", password="20001001", database="db")
cursor = conn.cursor()
cursor.callproc("p2", (3,))
print(cursor.fetchall())
执行结果为:
((4, 'yuanhao', 'female', 28, 202), (5, 'liwenzhou', 'male', 18, 200), (6, 'jingliyang', 'female', 18, 204), (8, 'Coco', 'female', 19, 200), (9, 'Bei', 'female', 2, 202), (11, 'Coco', 'female', 19, 200), (12, 'Bei', 'female', 2, 202), (13, 'Alex', 'male', 19, 200))
3.删除存储过程
drop procedure proc_name;