MySQL数据库程序设计(四)

前言

前面几个章节讲解了对数据库、表的创建、操作等一系列功能。本章节主要讲解视图、触发器、事件、存储过程与存储函数、访问控制与安全管理和备份与恢复。也是MySQL数据库设计的最后一个章节。

视图

视图是数据库系统中一种非常有用的数据库对象,它往往是和外模式联系在一起的概念。视图是一个或多个表组成的表,然而,视图不同于数据库中真实存在的表,其区别:

  • 虚拟表,非真实存在,建立在真实表的基础上,数据也是在真实表中。
  • 视图的内容是通过查询操作的SQL语句来定义的,并且这些数据会实时更新。
  • 视图不能索引,也不能有关联触发器,默认值。
  • 视图是用来查看别处数据的一种设施,本身不存储数据。

尽管视图与表本质上不同,但视图经过定义后,也可以如同使用表一样对视图进行查询以及受限的修改、删除、更新等操作。使用试图具有以下优点:

  • 集中分散数据。当用户所需的数据分散在多个表中,可以通过定义视图将这些数据集中在一起,方便用户查询。
  • 简化查询语句。定义视图可以为用户屏蔽数据的复杂性,不必详细了解表结构,即便底层数据库表发生更改。也不会影响用户对数据的使用,只需重新定义视图内容即可。实际上视图查询定义就是模式和外模式之间的映射关系,这样一来外模式编写的程序不要修改,保证了数据与程序的逻辑独立性。
  • 重用SQL语句。视图提供的是一种对查询操作的封装,本身不包含数据。真实表的数据修改后, 视图展示的就是更新后的数据。
  • 保护数据安全。通过授予用户使用视图权限,不具体指定使用表的权限,保护真实表的数据安全。
  • 共享所需数据。多个用户可以共享数据库数据。
  • 更改数据格式。通过SQL语句,将展示的数据按所需格式定义,此操作不会改变真实表结构。

创建视图

语法格式为:

CREATE [OR REPLACE] VIEW 视图名[(列名1,...,列名N)] 
AS
SELECT 目标表达式列1,...,目标表达式列N FROM 表名
WHERE 条件表达式
[WITH [CASCADED | LOCAL] CHECK OPTION];

定义好视图后通过AS关键字将查询SQL语句将视图建立起来,查询语句和之前一样用法。OR REPLACE可选项。会替换已存在的同名视图,需要在试图上拥有删除权限。WITH CHECK OPTION可选项,更新视图都需要符合查询语句中的限制条件,其中CASCADED为默认选项值,对所有试图进行检查,LOCAL则只对定义CHECK OPTION的视图进行检查。

比如:建立一个学生姓名、班主任、班级号的视图。执行结果如下所示:

mysql> create view v_student_tech_class(student_name,tech_name,class_no)
    -> as
    -> select student_name,tech_name,class.class_no from student,tech,class
    -> where student.class_no = class.class_no and class_tech_no = tech_no;
Query OK, 0 rows affected (0.01 sec)

创建好视图后,可以通过SQL查看视图机构,语法格式为:

SHOW CREATE VIEW 视图名;

查询刚刚建立好的试图结构,执行结果如下所示:

mysql> show create view v_student_tech_class\G;
*************************** 1. row ***************************
                View: v_student_tech_class
         Create View: CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` 
         SQL SECURITY DEFINER VIEW `v_student_tech_class` (`student_name`,`tech_name`,`class_no`) AS
         select `student`.`student_name` AS `student_name`,`tech`.`tech_name` AS `tech_name`,`class`.`class_no` AS `class_no` 
         from ((`student` join `tech`) join `class`) 
         where ((`student`.`class_no` = `class`.`class_no`) and (`class`.`class_tech_no` = `tech`.`tech_no`))
character_set_client: utf8
collation_connection: utf8_general_ci
1 row in set (0.00 sec)

查询视图

查询、修改、新增等操作视图的语句和操作表一致,这里不再做详细讲解。查询刚刚创建的视图,执行结果如下所示:

mysql> select * from v_student_tech_class;
+--------------+-----------+----------+
| student_name | tech_name | class_no |
+--------------+-----------+----------+
| 张小明       | 王老师    |        4 |
| 小米         | 张老师    |        2 |
| 小敏         | 张老师    |        2 |
| 小蜜         | 李老师    |        3 |
| 朱晓明       | 谢老师    |        1 |
| 周润法       | 谢老师    |        1 |
+--------------+-----------+----------+
6 rows in set (0.00 sec)

需要注意,并非所有视图都可以进行INSERTUPDATEDELETE等操作,视图和表之间为一对一关系。比如包含以下SQL语句结构的视图是不可更新的:

  • 聚合函数、DISTINCT关键字、GROUP BY 子句、ORDER BY子句、HAVING子句。
  • UNION运算符、子查询、FROM包含多个表、SELECT包含不可更新的视图。

不建议通过视图对数据进行更新操作,如需要请对指定表进行操作,视图的定义本就是来做复杂查询的。

修改视图

修改视图和创建视图大致上差不多。语法格式为:

ALTER VIEW 视图名[(列名1,...,列名N)] 
AS
SELECT 目标表达式列1,...,目标表达式列N FROM 表名
WHERE 条件表达式
[WITH [CASCADED | LOCAL] CHECK OPTION];

比如:修改视图条件,只查询老师名为“谢老师”的学生、班级。执行结果如下所示:

mysql> alter view v_student_tech_class(student_name,tech_name,class_no)
    -> as
    -> select student_name,tech_name,class.class_no from student,tech,class
    -> where student.class_no = class.class_no and class_tech_no = tech_no and tech_name='谢老师';
Query OK, 0 rows affected (0.01 sec)

mysql> select * from v_student_tech_class;
+--------------+-----------+----------+
| student_name | tech_name | class_no |
+--------------+-----------+----------+
| 朱晓明       | 谢老师    |        1 |
| 周润法       | 谢老师    |        1 |
+--------------+-----------+----------+
2 rows in set (0.00 sec)

删除视图

当视图废弃不用时,可以删除视图。语法格式为:

DROP VIEW [IF EXISTS] 视图名;

比如:删除刚刚创建的名为v_student_tech_class视图,执行结果如下所示:

mysql> drop view v_student_tech_class;
Query OK, 0 rows affected (0.01 sec)

mysql> select * from v_student_tech_class;
ERROR 1146 (42S02): Table 'school.v_student_tech_class' doesn't exist

触发器

触发器是一个被指定关联到一个表的数据库对象,当特定事件触发时,将被激活操作。

创建触发器

语法格式为:

CREATE TRIGGER 触发器名 触发时间 触发事件
ON 表名 FOR EACH ROW 触发主体;
  • 触发时间。有两个选项BEFORAFTER,表示在激活它的语句之前或之后触发。
  • 触发事件。有以下操作可以激活触发器:INSERT语句、UPDATE语句、DELETE语句。
  • FOR EACH ROW。声明每一行都会激活触发器动作。
  • 触发主体。表示要执行的MySQL语句,执行多个语句可以使用BEGIN...END复合语句结构。

一个表中不能拥有两个相同触发时刻和事件的触发器,比如:一个表不能有两个BEFORE UPDATE触发器。

比如:创建一个学生表的触发器,每次向学生表插入时,可以通过通过变量查询学生姓名。执行结果如下所示:

mysql> create trigger stu_trigger after insert on student for each row
    -> set @str = new.student_name;
Query OK, 0 rows affected (0.01 sec)

mysql> select @str;
+------------+
| @str       |
+------------+
| NULL       |
+------------+
1 row in set (0.00 sec)

mysql> insert into student values(9,'张一天',11,4);
Query OK, 1 row affected (0.01 sec)

mysql> select @str;
+-----------+
| @str      |
+-----------+
| 张一天    |
+-----------+
1 row in set (0.00 sec)

触发器中有两个关键字OLDNEWOLD表示原来的值,只有在DELETEUPDATE触发时才能使用,而且OLD的值是只读,不能被更新;NEW表示新的值,只有在INSERTUPDATE触发时使用,NEW的值是可以被更新的。

删除触发器

当业务逻辑变动时,应及时删除触发器,避免照成不必要的问题。语法格式为:

DROPP TRIGGER [IF EXISTS] 触发器名;

删除触发器需要SUPER权限。触发器不支持更新,所以只删除后重新创建。

比如:将刚才创建的触发器删除。执行结果如下所示:

mysql> drop trigger if exists stu_trigger;
Query OK, 0 rows affected (0.02 sec)

删除完后,可以通过查看触发器,来判断是否清除干净。语法格式为:

SHOW TRIGGERS [{
   
   FROM | IN} 数据库名]

执行结果如下所示:

mysql> show triggers from school;
Empty set (0.00 sec)

事件

事件称为事件调度器,可以在指定时刻执行某些特定的任务。

在使用事件调度器之前,须确保事件调度器已被开启,执行结果如下所示:

mysql> show variables like 'event_scheduler';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| event_scheduler | ON    |
+-----------------+-------+
1 row in set, 1 warning (0.00 sec)

或者

mysql> select @@event_scheduler;
+-------------------+
| @@event_scheduler |
+-------------------+
| ON                |
+-------------------+
1 row in set (0.00 sec)

如果没有开启,可以通过以下命令开启该功能:

mysql> set global event_scheduler =1;
Query OK, 0 rows affected (0.00 sec)

或者

mysql> set global event_scheduler =true;
Query OK, 0 rows affected (0.00 sec)

mysql8.0默认开启,如果还未开启,可以在MySQL的my.ini中[mysqld]下加上“event_scheduler = ON”,即可永久生效。

创建事件

语法格式为:

CREATE EVENT [IF NOT EXISTS] 事件名
ON SCHEDULER schedule
[ ENABLE | DISABLE | DISABLE ON SLAVE]
ON 事件主体;

schedule语法格式为:

[AT timestamp [+INTERVAL interval]... 
| EVERY interval 
[STARTS timestamp [+INTERVAL interval]...]
[ENDS timestamp [+INTERVAL interval]...]
]

interval语法格式为:

quantity {
   
   YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE | WEEK | SECOND |YEAR_MONTH
			| DAY_HOUR | DAY_MINUTE | DAY_SECOND | HOUR_MINUTE | HOUR_MINUTE |HOUR_SECOND 
			| MINUTE_SECOND}

AT语句:用于指定事件某个时段发生。timestamp 表示具体时间点;interval表示事件间隔;quantity表示间隔时间数值。
EVERY语句:表示时间在指定时间内每间隔多长时间执行以此,STARTS 表示指定开始时间,ENDS 表示指定结束时间。
事件主体:表示要执行的MySQL语句,执行多个语句可以使用BEGIN...END复合语句结构。
ENABLE | DISABLE | DISABLE ON SLAVE:可选项。ENABLE表示事件是活动的,DISABLE表示事件是关闭的,DISABLE ON SLAVE表示在从机中是关闭,如果不指定三个选项中任意一个,则在事件创建后,变为活动的。

比如:创建一个指定时间向表里插入数据。执行结果如下所示:

mysql> create event ev_1 on schedule at '2021-09-21 16:45:00'
    -> do
    -> insert into student values('100','事件学生',100,1);
Query OK, 0 rows affected (0.01 sec)

mysql> select * from student;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
|          1 | 张明         |          14 |        4 |
|          2 | 小米         |          13 |        2 |
|          3 | 小敏         |          11 |        2 |
|          4 | 小蜜         |          10 |        3 |
|          7 | 朱晓明       |          14 |        1 |
|          8 | 周润法       |        NULL |        1 |
|          9 | 张一天       |          11 |        4 |
|        100 | 事件学生     |         100 |        1 |
+------------+--------------+-------------+----------+
8 rows in set (0.00 sec)

由于时间短,所以很快可以看到结果。比如:删除刚创建数据,1分钟后执行且在指定时间内结束。执行结果如下所示:

mysql> create event ev_1 on schedule every 1 minute
    -> starts curdate()+interval 1 minute
    -> ends '2021-09-21 16:56:00'
    -> do
    -> delete from student where student_no = 100;
Query OK, 0 rows affected (0.01 sec)

mysql> select * from student;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
|          1 | 张明         |          14 |        4 |
|          2 | 小米         |          13 |        2 |
|          3 | 小敏         |          11 |        2 |
|          4 | 小蜜         |          10 |        3 |
|          7 | 朱晓明       |          14 |        1 |
|          8 | 周润法       |        NULL |        1 |
|          9 | 张一天       |          11 |        4 |
+------------+--------------+-------------+----------+
7 rows in set (0.00 sec)

创建好事件后,可以查看事件。语法格式为:

SHOW CREATE EVENT 事件名;

比如:查看刚刚创建的事件。执行结果如下所示:

mysql> show create event ev_1\G;
*************************** 1. row ***************************
               Event: ev_1
            sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
           time_zone: SYSTEM
        Create Event: CREATE DEFINER=`root`@`localhost` EVENT `ev_1` ON SCHEDULE EVERY 1 MONTH STARTS '2021-09-21 00:01:00' ENDS '2021-10-21 16:56:00' ON COMPLETION NOT PRESERVE ENABLE DO delete from student where student_no = 100
character_set_client: utf8mb3
collation_connection: utf8_general_ci
  Database Collation: gb2312_chinese_ci
1 row in set (0.00 sec)

修改事件

创建好事件后,发现问题,可以修改事件。语法格式为:

ALTER EVENT [IF NOT EXISTS] 事件名
ON SCHEDULER schedule
[RENAME TO 新试图名]
[ ENABLE | DISABLE | DISABLE ON SLAVE]
ON 事件主体;

语法格式和创建事件语法一致,不做重复讲解。

比如:将事件ev_1修改为event_delete。执行结果如下所示:

mysql> alter event ev_1 rename to event_delete;
Query OK, 0 rows affected (0.01 sec)

删除事件

语法格式为:

DROP EVENT [IF EXISTS] 事件名;

比如:删除event_delete事件。执行结果如下所示:

mysql> drop event event_delete;
Query OK, 0 rows affected (0.01 sec)

mysql> show create event ev_1\G;
ERROR 1539 (HY000): Unknown event 'ev_1'

存储过程与存储函数

存储过程是一组为了完成某个特定功能的SQL语句集,就是存放在数据库中的代码,经过编译后存储在数据库中,只需调用即可,提高执行效率,增强灵活性,减少网络流量,保证数据库的完全性和完整性。

存储过程

创建存储过程

语法格式为:

DELIMITER $$
CREATE PEOCEDURE 存储过程名([IN|OUT|INOUT]参数名 参数类型)
存储过程主体

IN|OUT|INOUTIN表示输入、OUT表示输出、INOUT表示输入输出。
DELIMITER :表示结束符,可以自定义为任何符号,避免使用反斜杠“\”转义符。
存储过程主体:执行SQL时以BEGIN开始,END结束,若只有一条SQL可以省略,BEGIN...END还可以嵌套使用。

存储过程体

存储过程体可以使用各种SQL语句与过程式语句组合,来封装数据库应用中复杂的业务逻辑和处理规则。

  1. 局部变量

用来存储过程体中临时结果,还可以赋予初始值。语法格式为:

DECLARE 变量名 类型 [DEFAULT 默认值];

比如:声明一个局部变量。执行结果如下所示:

DECLARE sno CHAR(10);

局部变量只能在BEGIN...END的中开头处声明,只能内部调用,不同于用户变量没有“@”字符,用户变量可以全局使用。

  1. SET语句
    为局部变量赋值。

语法格式为:

SET 变量名 =;

比如:给sno赋值。执行结果如下所示:

SET sno='202100901';
  1. SELECT...INTO语句
    把选定列的值存储到局部变量中,且只有一条结果集。语法格式为:
SELECT 列名1,...,列名N INTO 赋值变量名1,...,赋值变量名N FROM ...

比如:将学生姓名为“朱晓明”的姓名和班级赋值给变量。执行结果如下所示:

select student_name,calss_no into student_name,class_no from student where student_name ='朱晓明';
  1. 流程控制语句

(1)条件判断语句

判断条件有IFCASE语句,可以根据不同的条件执行不同的操作。

IF语句的语法格式为:

IF 判断条件 THEN 语句
    [ELSEIF 判断条件 THEN 语句]...
    [ELSE 语句]
END IF;

当条件为真时,才会执行相应的SQL语句。

比如:判断学生年龄,通过不同判断对应变量累加。执行结果如下所示:

IF student_age>20 THEN SET @count1=@count1+1;
    ELSEIF student_age=20 THEN @count2=@count2+1;
    ELSE @count3=@count3+1;
END lF;

CASE语句的语法格式为:

CASE 判断的变量
    WHEN 比较的值 THEN 语句
    [WHEN 比较的值 THEN 语句]...
    [ELSE 执行语句]
END CASE;

CASE
    WHEN 判断条件 THEN 语句
    [WHEN 判断条件 THEN 语句] ...
    [ELSE 语句]
END CASE;

比如:判断学生年龄,通过不同判断对应变量累加。执行结果如下所示:

CASE student_age
    WHEN 20 THEN SET @count1=@count1+1;
    ELSE SET @count2=@count2+1;
END CASE;

CASE
    WHEN student_age=20 THEN SET @count1=@count1+1;
    ELSE SET @count2=@count2+1;
END CASE;

(2)循环条件
循环多条数据。常见的有WHILE语句、REPEAT语句、LOOP语句、LEAVE 语句等。

WHILE的语法格式为:

[begin_label:] WHILE 遍历条件 DO
    语句
END WHILE [end label];

只有条件为真时,才会执行SQL语句,直到判断结果不为真结束循环。
begin_labelend label:标注,可选项,必须成对出现。

比如:count变量累计加+1,直到大于或等于100结束循环。执行结果如下所示:

WHILE @count<100 DO
    SET @count=@count+1;
END WHILE;

REPEAT语法格式为:

[begin_label:] REPEAT
    语句
    UNTIL 结束循环的条件
END REPEAT [end_label];

REPEAT语句先执行在判断,当条件为真,结束循环,否则继续循环。

比如:count变量累计加1,循环100次结束。执行结果如下所示:

REPEAT
    SET @count=@count+1;
    UNTIL @count=100
END REPEAT;

LOOP语法结构为:

[begin_label:]LOOP
    语句
END LOOP [end_label];

LOOP语句没有跳出循环的语句,所以会一直循环,执行结果如下所示:

add_num:LOOP
    SET @count=@count+1;
END LOOP add_num;

LEAVE 主要用于跳出循环,语法格式为:

LEAVE label;

比如:当循环100次后,跳出循环。执行结果如下所示:

add_num:LOOP
    SET @count=@count+1;
    IF @count=100 THEN
        LEAVE add_num;
END LOOP add num;
  1. 游标

游标可以存储多条结果集。在BEGIN...END语句块中可以定义多个游标,但是游标名是唯一的。
(1)声明游标
使用游标之前,先定义它。语法格式为:

DECLARE 游标名 CURSOR FOR 查询语句;

(2)打开游标

打开游标后才能使用游标。语法格式为:

OPEN 游标名;

(3)读取数据
对填有数据的游标,可根据需要取出数据。语法格式为:

FETCH 游标名 INTO 赋值变量名1,...,赋值变量名N ...;

(4)关闭游标

使用完游标一定要关闭游标,否则浪费资源。语法格式为:

CLOSE 游标名;

调用存储过程

创建好存储过程后,调用存储过程才能执行操作。语法格式为:

CALL 存储过程名([参数1,...,参数N]);

介绍完存储过程的使用后,创建一个根据班级编号,统计班级全部学生总年龄的存储过程。执行结果如下所示:

mysql> delimiter $$;
    -> create procedure sum_age(in p_class int,out sum_age int)
    -> begin
    -> declare done boolean DEFAULT 0;# 异常处理
    -> declare number int DEFAULT 0;# 接受游标值
    -> declare sum int DEFAULT 0;# 累计遍历
    -> declare cur cursor for select student_age from student where class_no = p_class;# 通过班号查询
    -> declare continue handler for sqlstate'02000' SET done = 1;# 设置终止标志
    -> open cur;# 打开游标
    -> while done<>1 do# 遍历
    -> fetch cur into number; #将游标的结果集赋值给遍历
    -> if done<>1 then # 防止多遍历一次
    -> set sum = sum+number;# 累计学生年龄
    -> end if;# 结束if判断
    -> end while;# 结束循环
    -> set sum_age= sum;# 将统计结果赋值给输出变量
    -> close cur;# 关闭游标
    -> end $$;# 结束
    
mysql> call sum_age(2,@sum);
Query OK, 0 rows affected (0.00 sec)

mysql> select @sum;
+------+
| @sum |
+------+
|   24 |
+------+
1 row in set (0.00 sec)

删除存储过程

语法格式为:

DROP PROCEDURE [IF EXISTS] 存储过程名;

比如:删除刚刚创建的存储过程sum_age。执行结果如下所示:

mysql> drop procedure sum_age;
Query OK, 0 rows affected (0.01 sec)

mysql> show create procedure sum_age;
ERROR 1305 (42000): PROCEDURE sum_age does not exist

语句SHOW CREATE PROCEDURE 存储过程名,可以用来查询存储过程。

存储函数

与存储过程十分相似,不过还是有几点区别:

  • 不过存储函数不能拥有输出参数,本身就是输出参数。
  • 使用SELECT语句对存储函数进行调用。
  • 存储函数必须包含一条RETURN语句,不能在存储过程中使用。

创建存储函数

语法格式为:

CREATE FUNCTION 存储函数名(参数名 参数类型)
RETURNS 返回值类型
存储函数主体

比如:把存储过程的代码,用存储函数重新编写一遍。执行结果如下所示:

mysql> create function sum_age(p_class int)
    -> returns int
    -> deterministic
    -> begin
    -> declare done boolean DEFAULT 0;
    -> declare number int DEFAULT 0;
    -> declare sum int DEFAULT 0;
    -> declare cur cursor for select student_age from student where class_no = p_class;
    -> declare continue handler for sqlstate'02000' SET done = 1;
    -> open cur;
    -> while done<>1 do
    -> fetch cur into number;
    -> if done<>1 then
    -> set sum = sum+number;
    -> end if;
    -> end while;
    -> return(select sum);
    -> close cur;
    -> end $$;
Query OK, 0 rows affected (0.01 sec)

mysql> select sum_age(2);
+------------+
| sum_age(2) |
+------------+
|         24 |
+------------+
1 row in set (0.00 sec)

删除存储函数

语法格式为:

DROP FUNCTION [IF EXISTS] 存储过程名;

比如:删除刚刚创建的存储函数sum_age。执行结果如下所示:

mysql> drop function sum_age;
Query OK, 0 rows affected (0.01 sec)

mysql> show create function sum_age;
ERROR 1305 (42000): FUNCTION sum_age does not exist

和存储过程一样没区别,使用SHOW CREATE FUNTION 存储函数名,查看存储函数。

访问控制与安全

访问控制实际上就是为用户提供且仅提供他们所需的访问权,以此确保MySQL服务器的安全访问。

用户账户管理

MySQL相关的信息都存储在一个名为mysql数据库的user表中,可以通过SELECT语句查看有哪些用户。执行结果如下所示:

mysql> select user from mysql.user;
+------------------+
| user             |
+------------------+
| mysql.infoschema |
| mysql.session    |
| mysql.sys        |
| root             |
+------------------+
4 rows in set (0.00 sec)

可以看到表中有很多系统默认的用户,为了避免操作不当,应尽可能少用root账号登录系统,通过创建适当权限的账号来访问数据库进行对应操作。

创建用户账号

语法格式为:

CREATE USER 用户名@主机名 [IDENTIFIED BY [ PASSWORD ] '密码'], ...

在创建过程中只给了用户,没指定主机名,则默认主键名为“%”,表示一组主机。IDENTIFIED BY语句可选项。指定的用户账户的口令。PASSWORD 语句可选项,指定散列口令,如果使用明文设置口令,则忽略此语句;创建多个用户用逗号分开。

比如:以MySQL5.7为例,创建两个用户名为“zhangsan”和“lisi”,主机名为localhsot,口令一个明文一个加密。执行结果如下所示:

mysql> CREATE USER 'zhangsan'@'localhost' IDENTIFIED BY 'test1';
Query OK, 1 rows affected (0.06 sec)

mysql> SELECT password('test1');
+-------------------------------------------+
| password('test1')                         |
+-------------------------------------------+
| *06C0BF5B64ECE2F648B5F048A71903906BA08E5C |
+-------------------------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> CREATE USER 'lisi'@'localhost'IDENTIFIED BY PASSWORD '*06C0BF5B64ECE2F648B5F048A71903906BA08E5C';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> select user from mysql.user;
+------------------+
| user             |
+------------------+
| lisi             |
| mysql.infoschema |
| mysql.session    |
| mysql.sys        |
| root             |
| zhangsan         |
+------------------+
6 rows in set (0.00 sec)

SELECT PASSWORD()语句可以查看加密的密码;创建用户角色需要拥有INSERT权限或创建用户权限;如果用户名相同主机名不同,视为不同用户;创建用户可以不知道口令登录,从安全角度不建议这样。

修改用户账号

语法格式为:

RENAME USER 原用户名@主机名 TO 新用户名@主机名,...;

多个用户名修改用逗号分开。

比如:将“lisi”的用户名改为“xiaoliu”。执行结果如下所示:

mysql> rename user lisi@localhost to xiaoliu@localhost;
Query OK, 0 rows affected (0.00 sec)

既然可以修改账户,当然也可以修改用户的口令。语法格式为:

SET PASSWORD FOR 用户名@主机名=密码;

比如:修改“zhangsan”的用户密码。执行结果如下所示:

mysql> set password for zhangsan@localhost='abc123';
Query OK, 0 rows affected (0.00 sec)

删除用户

语法格式为:

DROP USER 用户名@主机名,...;

可以删除多个用户,用逗号分隔;删除用户需要拥有DELETE权限或全局创建用户权限;删除的用户不会影响之前对表的任何操作。

比如:删除“xiaoliu”用户。执行结果如下所示:

mysql> drop user xiaoliu@localhost;
Query OK, 0 rows affected (0.00 sec)

mysql> select user from mysql.user;
+------------------+
| user             |
+------------------+
| mysql.infoschema |
| mysql.session    |
| mysql.sys        |
| root             |
| zhangsan         |
+------------------+
5 rows in set (0.00 sec)

账户权限管理

创建用户账户后,需为用户分配适当的访问权限,新创建的用户账户没有访问权限,只能登陆MySQL服务器,不能执行任何操作。我们可以查看刚刚创建的“zhangsan”的用户权限。执行结果如下所示:

mysql> show grants for zhangsan@localhost;
+----------------------------------------------+
| Grants for zhangsan@localhost                |
+----------------------------------------------+
| GRANT USAGE ON *.* TO `zhangsan`@`localhost` |
+----------------------------------------------+
1 row in set (0.00 sec)

USAGE 表示只有登录权限。

权限授予

GRANT priv_type
ON level
TO 用户名@主机名 ,...
[WITH with_option [with_option]...]

priv_type:用于指定权限,如INSERTUPDATEDELETE等数据库操作。

ON子句:赋予对象权限。

level指定权限级别,一共有四种权限:列权限、表权限、数据库权限、用户权限:

*|*.*|db_name.*|db_name.tb_name|tbl_name|db_name.routine_name

对应权限

所有表|所有库.所有表|具体库.所有表|具体库.具体表|某个表或视图|具体库.存储过程或函数

TO子句:指定被授权的用户。

WITH子句:实现权限转移。

with_option:设置用户权限选项:

  • GRANT OPTION:被授权的用户可以将这些权限赋予给别的用户;
  • MAX_QUERIES_PER_HOUR count:设置每个小时可以允许执行 count 次查询;
  • MAX_UPDATES_PER_HOUR count:设置每个小时可以允许执行 count 次更新;
  • MAX_CONNECTIONS_PER_HOUR count:设置每小时可以建立 count 个连接;
  • MAX_USER_CONNECTIONS count:设置单个用户可以同时具有的 count 个连接。

比如:设置“zhangsan”账户对school数据库的所有表的INSERT、UPDATE、DELETE权限。执行结果如下所示:

mysql> grant select,update,insert
    -> on school.*
    -> to zhangsan@localhost
    -> ;
Query OK, 0 rows affected (0.01 sec)

mysql> show grants for zhangsan@localhost;
+----------------------------------------------------------------------+
| Grants for zhangsan@localhost                                        |
+----------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `zhangsan`@`localhost`                         |
| GRANT SELECT, INSERT, UPDATE ON `school`.* TO `zhangsan`@`localhost` |
+----------------------------------------------------------------------+
2 rows in set (0.00 sec)

当然还可以指定某个字段的操作,比如:SELECT(列名)UPDATE(列名)等。有兴趣的小伙伴可以自己去尝试。

权限的转移与限制

  1. 权限转移
    WITH子句指定为WITH GRANT OPTION,则表示指定用户可以把自己拥有的权限授予其他用户的权限。

比如:授予一个用户“test1”拥有数据库school的SELECT权限,并允许授予给其他用户此权限。执行结果如下所示:

mysql> grant select
    -> on school.*
    -> to test1@localhost
    -> with grant option;
Query OK, 0 rows affected (0.00 sec)
  1. 权限限制

WITH子句,不仅可以权限转移还可以对账号操作次数进行限制。

比如:给“test2”用户赋予每小时查询一次的权限(MySQL8.0语法有所改动)。执行结果如下所示:

mysql> grant select
    -> on school.*
    -> to test2@localhost
    -> with max_queries_per_hour 1;
Query OK, 0 rows affected (0.00 sec)

权限的撤销

当不希望删除用户时,可以撤销用户权限。语法格式为:

REVOKE priv_type
ON level
FROM 用户名@主机名,...

REVOKE ALL PRIVILEGES, GRANT OPTION FROM 用户名@主机名,...

第一种用于回收某些特定权限,第二种用于回收全部权限。

比如:回收“zhangsan”用户的新增和删除权限。执行结果如下所示:

mysql> revoke update,delete
    -> on school.*
    -> from zhangsan@localhost;
Query OK, 0 rows affected (0.01 sec)

mysql> show grants for zhangsan@localhost;
+--------------------------------------------------------------+
| Grants for zhangsan@localhost                                |
+--------------------------------------------------------------+
| GRANT USAGE ON *.* TO `zhangsan`@`localhost`                 |
| GRANT SELECT, INSERT ON `school`.* TO `zhangsan`@`localhost` |
+--------------------------------------------------------------+
2 rows in set (0.00 sec)

备份与恢复

在MySQL数据库日常管理中,存在人为或者非人为因素,导致数据库损坏或者破坏数据的情况。通常会进行数据库的备份和恢复来保证数据库中数据的可靠性和完整性。

备份与恢复的方法

数据库的备份是指通过导出数据或复制表文件的方式来制作数据库副本。数据库恢复则是当数据库出现故障或遭到破坏时,将备份数据库加载到系统,从而使数据库从错误状态恢复到备份时的正确状态。

使用SQL语句备份和恢复数据

  1. 备份数据

语法格式为:

SELECT * FROM 表名 INTO OUTFILE 文件名 [CHARACTER SET 字符集]
[FIELDS
[TERMINATED BY 'string']
[ENCLOSED BY 'char']
[ESCAPED BY 'char']
]
[LINES TERMINATED BY 'string']

将表中数据导出到指定路径的文件名的文件中。FIELDS子句分别有三个亚子句,指定FIELDS子句,必须要求指定其中一个亚子句。TERMINATED BY子句用来指定字段之间的符号;ENCLOSED BY子句用来包裹字段的符号;ESCAPED BY子句用来转义字符;LINES TERMINATED BY子句用于指定数据行结束的符号。

比如:不指定导出格式和指定格式两种方式来导出学生表所有数据。执行结果如下所示:

mysql> select * from student into outfile 'd:/mnt/student.txt';
Query OK, 7 rows affected (0.00 sec)

在这里插入图片描述
不指定格式是不加字段符号、空格隔开、换行,右斜杠转义,导出的数据。

mysql> select * from student into outfile 'd:/mnt/student.txt'
    -> fields terminated by ','
    -> enclosed by '"'
    -> escaped by '*'
    -> lines terminated by '\r\n';
Query OK, 7 rows affected (0.00 sec)

在这里插入图片描述
指定格式为逗号分隔、双引号包裹字符、换行、*号转移,导出的数据。

  1. 恢复数据

语法格式为:

LOAD DATA INFILE 文件名 
INTO TABLE 表名
[FIELDS
[TERMINATED BY 'string']
[ENCLOSED BY 'char']
[ESCAPED BY 'char']
]
[LINES TERMINATED BY 'string']

和导出SQL语句没啥区别,恢复数据是要注意导出数据的格式。

比如:删除学生表数据,然后将刚刚导出的数据进行恢复。执行结果如下所示:

mysql> delete from student;
Query OK, 7 rows affected (0.01 sec)

mysql> select * from student;
Empty set (0.00 sec)

mysql> load data infile 'd:/mnt/student.txt'
    -> into table student
    -> fields terminated by ','
    -> enclosed by '"'
    -> escaped by '*'
    -> lines terminated by '\r\n';
Query OK, 7 rows affected (0.01 sec)
Records: 7  Deleted: 0  Skipped: 0  Warnings: 0

mysql> select * from student;
+------------+--------------+-------------+----------+
| student_no | student_name | student_age | class_no |
+------------+--------------+-------------+----------+
|          1 | 张明         |          14 |        4 |
|          2 | 小米         |          13 |        2 |
|          3 | 小敏         |          11 |        2 |
|          4 | 小蜜         |          10 |        3 |
|          7 | 朱晓明       |          14 |        1 |
|          8 | 周润法       |        NULL |        1 |
|          9 | 张一天       |          11 |        4 |
+------------+--------------+-------------+----------+
7 rows in set (0.00 sec)

另外,多个用户使用MySQL数据库的情况下,备份时需要使用LOCK TABLES 表名 TRAD 语句将表进行读锁定,防止在备份时被其他用户更新;恢复数据时,使用LOCK TABLES 表名 WRITE语句将表进行写锁定,避免数据冲突。恢复完成后使用UNLOCK TABLES 语句对表进行解锁。

使用MySQL客户端程序备份和恢复数据

MySQL提供许多免费使用的客户端,在安装目录的bin目录中。此操作非MySQL语句,所以在MySQL中使用是无效的,只需要到bin目录下即可,如图:
在这里插入图片描述

  1. 备份数据库
    mysqldump命令可以备份所有数据库、指定数据库和指定表,注意不需要分号结尾。语法格式为:
    (1)备份所有数据库
mysqldump 登录账号 --dall-databases > 文件地址

比如:导出当前所有数据库。执行结果如下所示:

D:\mysql\bin>mysqldump -uroot -p --all-databases >d:\mnt\all.sql
Enter password:

在这里插入图片描述

(2)备份指定数据库

mysqldump 登录账号 --databases 数据库名,... > 文件地址

比如:导出school数据库。执行结果如下所示:

D:\mysql\bin>mysqldump -uroot -p  --databases school >d:\mnt\school.sql
Enter password:

在这里插入图片描述

(3)备份指定表

mysqldump 登录账号 数据库名 表名 > 文件地址

比如:导出student表。执行结果如下所示:

D:\mysql\bin>mysqldump -uroot -p school student>d:\mnt\student.sql
Enter password:

在这里插入图片描述

  1. 恢复数据库
    恢复数据库有两种方式:使用mysql命令恢复、使用mysqlimport命令恢复。

(1)mysql命令恢复
可以恢复数据库的数据结构。

语法格式为:

mysql 登录账号 [数据库名] < 文件名

可以不用指定数据库名。

比如:先删除school数据库,再恢复。执行结果如下所示:

mysql> drop database school;
Query OK, 4 rows affected (0.05 sec)

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.00 sec)

D:\mysql\bin>mysql -u root -p < d:\mnt\school.sql
Enter password:

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| school             |
| sys                |
+--------------------+
1 rows in set (0.00 sec)

当然还有很多恢复数据库的命令比如:登录mysql后使用SOURCE 文件名 ,有兴趣可以自己去尝试。

(2)mysqlimport命令恢复
只能恢复数据,不能进行结构恢复。本质上还是用的LOAD DATA...INFILE语句。

语法格式为:

mysqlimpot 登录账号 数据库名 文件名

比如:先删除student表的数据,再恢复。执行结果如下所示:

mysql> drop table student;
Query OK, 0 rows affected (0.01 sec)

mysql> show tables;
+------------------+
| Tables_in_school |
+------------------+
| class            |
| student_copy     |
| tb_student_1     |
| tech             |
+------------------+
4 rows in set (0.00 sec)

D:\mysql\bin>mysqlimport -u root -p school d:\mnt\student.sql
Enter password:

mysql> show tables;
+------------------+
| Tables_in_school |
+------------------+
| class            |
| student          |
| student_copy     |
| tb_student_1     |
| tech             |
+------------------+
5 rows in set (0.00 sec)

还有很多种备份和恢复数据的工具,比如图形化操作,有兴趣的可以自己去了解,操作很方便。

二进制日志文件的使用

数据库管理员不会无时无刻备份数据。因此,当数据丢失或破坏时,可以通过二进制日志来查看操作,MySQL5.5以后已经被二进制日志取代,保存数据的更改操作记录,不包括查询记录。

开启日志文件

二进制日志会影响系统性能,浪费资源,所有需要手动开启,8.0版本好像默认开启,文件保存在data目录种。具体操作如下:
(1)打开mysql安装目录下的my.ini文件(linux则打开,my.cnf文件)
(2)找到[mysqld]标签,加入一行代码log-bin[=文件名]。文件名可选项,可以指定文件目录,默认主机名,每次启动服务器会重新生成一个二进制日志文件。
(3)保存修改,重启服务器。在MySQL安装目录的DATA文件夹下可以看到两个文件:文件名.数字编号、文件名.index。一个是二进制日志文件,以二进制存储;一个是二进制索引包含二进制日志文件名。

使用日志处理工具

  1. 查看二进制日志文件

MySQL自带二进制日志处理工具 mysqlbinlog,语法格式为:

mysqlbinlog 日志名

比如:查看系统binlog.000001的二进制文件内。执行结果如下所示:

D:\mysql\bin> mysqlbinlog D:\mysql-8.0.26-winx64\data\binlog.000001;
# The proper term is pseudo_replica_mode, but we use this compatibility alias
# to make the statement usable on server versions 8.0.24 and older.
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
DELIMITER /*!*/;
mysqlbinlog: File 'D:\mysql-8.0.26-winx64\data\binlog.000001;' not found (OS errno 2 - No such file or directory)
ERROR: Could not open log file
SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
DELIMITER ;
# End of log file
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

由于二进制数据非常庞大,可以保存到一个文本文件种,以便查看。执行结果如下所示:

D:\mysql-8.0.26-winx64\bin>mysqlbinlog D:\mysql-8.0.26-winx64\data\binlog.000001 > d:\mnt\all.txt

部分内容,如图所示:

在这里插入图片描述
当然mysql命令也有查看日志的方法。语法格式为:

SHOW BINLOG EVENTS IN 日志名;

比如:先查询mysql所有的日志文件,再查看最新的日志文件,再查看详情。执行结果如下所示:

mysql> show binary logs;
+---------------+-----------+-----------+
| Log_name      | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000001 |     25087 | No        |
| binlog.000002 |     12350 | No        |
| binlog.000003 |       179 | No        |
| binlog.000004 |     36825 | No        |
| binlog.000005 |   3632465 | No        |
| binlog.000006 |       179 | No        |
| binlog.000007 |       179 | No        |
| binlog.000008 |       179 | No        |
| binlog.000009 |       179 | No        |
| binlog.000010 |       179 | No        |
| binlog.000011 |       156 | No        |
+---------------+-----------+-----------+
11 rows in set (0.04 sec)

mysql> show master status;
+---------------+----------+--------------+------------------+-------------------+
| File          | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------+----------+--------------+------------------+-------------------+
| binlog.000011 |      156 |              |                  |                   |
+---------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

mysql> show binlog events in 'binlog.000011';
+---------------+-------+----------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Log_name      | Pos   | Event_type     | Server_id | End_log_pos | Info                                                                                                                                                                                                                                             |
+---------------+-------+----------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| binlog.000001 |     4 | Format_desc    |         1 |         125 | Server ver: 8.0.26, Binlog ver: 4                                                                                                                                                                                                                |
| binlog.000001 |   125 | Previous_gtids |         1 |         156 |                                                                                                                                                                                                                                                  |
| binlog.000001 |   156 | Anonymous_Gtid |         1 |         235 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS'                                                                                                                                                                                                             |
| binlog.000001 |   235 | Query          |         1 |         412 | create database school
default character set gb2312
default collate gb2312_chinese_ci /* xid=4 */
... ...
  1. 使用二进制日志恢复数据

语法格式为:

mysqlbinlog 日志名 | mysql账号

比如:恢复日志文件“binlog.000001”。执行结果如下所示:

D:\mysql-8.0.26-winx64\bin>mysqlbinlog D:\mysql-8.0.26-winx64\data\binlog.000001 | mysql -u root -p
Enter password:

由于二进制日志文件会占用很大硬盘资源,所以要及时清理没用的二进制日志文件。语法格式为:

RESET MASTER;

比如:清除本地的日志文件。执行结果如下所示:

mysql> show binary logs;
+---------------+-----------+-----------+
| Log_name      | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000001 |     25087 | No        |
| binlog.000002 |     12350 | No        |
| binlog.000003 |       179 | No        |
| binlog.000004 |     36825 | No        |
| binlog.000005 |   3632465 | No        |
| binlog.000006 |       179 | No        |
| binlog.000007 |       179 | No        |
| binlog.000008 |       179 | No        |
| binlog.000009 |       179 | No        |
| binlog.000010 |       179 | No        |
| binlog.000011 |     43787 | No        |
+---------------+-----------+-----------+
11 rows in set (0.00 sec)

mysql> reset master;
Query OK, 0 rows affected (0.03 sec)

mysql> show binary logs;
+---------------+-----------+-----------+
| Log_name      | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000001 |       156 | No        |
+---------------+-----------+-----------+
1 row in set (0.00 sec)

若要删除部分日志文件。语法格式为:

PURGE {MASTER |BINARY} LOGS TO 日志名

或删除指定时间之前的日志

PURGE {MASTER |BINARY} LOGS BEFORE 日期

执行结果如下所示:

mysql> purge master logs to 'binlog.000001';
Query OK, 0 rows affected (0.01 sec)

mysql> purge master logs before '2021-09-25 11:00:00';
Query OK, 0 rows affected, 1 warning (0.00 sec)

到这里MySQL数据设计的内容基本上都讲解完啦,有不对或者不懂的地方,欢迎评论区留言。

章节目录

上一篇:MySQL数据库程序设计(三)

猜你喜欢

转载自blog.csdn.net/qq_39940674/article/details/120425330