MySQL数据库期末复习②

索引

索引是数据库中用于加快数据检索速度的独立结构,类似于书的目录。下面从概念、原理到实战全面讲解。

类型 说明 示例
主键索引 特殊的唯一索引,非空,一张表只能有一个 PRIMARY KEY (id)
唯一索引 索引列的值必须唯一,允许 NULL UNIQUE INDEX idx_email (email)
普通索引 基本索引,无唯一性限制 INDEX idx_name (name)
全文索引 用于文本搜索,支持 MATCH ... AGAINST FULLTEXT INDEX ft_article (content)
空间索引 用于地理空间数据类型(POINT 等) SPATIAL INDEX sp_loc (location)
前缀索引 只索引列值的前缀部分,节省空间 INDEX idx_title (title(20))

创建与删除索引

sql 复制代码
-- 建表时创建
CREATE TABLE t (
  id INT PRIMARY KEY,          -- 主键索引
  name VARCHAR(50),
  email VARCHAR(100),
  INDEX idx_name (name),       -- 普通索引
  UNIQUE INDEX uk_email (email)-- 唯一索引
);

-- 建表后创建
CREATE INDEX idx ON 表名(列名);
ALTER TABLE 表名 ADD INDEX idx (列名);

-- 创建复合索引
CREATE INDEX idx_multi ON orders (user_id, status);

-- 删除索引
DROP INDEX idx ON 表名;
ALTER TABLE 表名 DROP INDEX idx;

最左前缀原则

对于复合索引 (col1, col2, col3),查询条件必须包含 col1 才能利用索引。例如:

sql 复制代码
-- 能利用索引
WHERE col1 = 1
WHERE col1 = 1 AND col2 = 2
WHERE col1 = 1 AND col2 = 2 AND col3 = 3

-- 不能利用索引(未从 col1 开始)
WHERE col2 = 2
WHERE col2 = 2 AND col3 = 3

覆盖索引

查询的所有列都在索引中,不需要回表,极大提升性能。

sql 复制代码
-- 假设有索引 (user_id, status)
SELECT user_id, status FROM orders WHERE user_id = 1;
-- 不需要回表

查看索引使用情况

使用 EXPLAIN 分析查询:

sql 复制代码
EXPLAIN SELECT * FROM student WHERE name = '张三';

关注以下字段:

  • key:实际使用的索引名(NULL 表示未使用)。
  • rows:扫描的行数。
  • type :连接类型,从优到差依次是:
    • system/const:主键或唯一索引等值查询,最快。
    • eq_ref:关联查询时使用主键或唯一索引。
    • ref:非唯一索引等值查询。
    • range:索引范围扫描(BETWEEN><IN)。
    • index:全索引扫描(比全表扫描稍好)。
    • ALL:全表扫描,最差,必须优化。
  • ExtraUsing index 表示覆盖索引,Using filesort 表示需要额外排序(可优化)。

全文索引而非 LIKE '%...%'

存储过程

存储过程将多条 SQL 语句封装成一个可重复调用的单元,存储在数据库服务器上。客户端只需通过 CALL 命令执行它,无需每次传输多条语句。

简单类比:就像编程语言中的函数,将经常使用的操作打包,输入参数就能得到结果。

创建存储过程

sql 复制代码
DELIMITER //
CREATE PROCEDURE 过程名([参数列表])
BEGIN
    -- SQL 语句
END //
DELIMITER ;

创建存储过程,删除一个指定学生记录。

sql 复制代码
USE xscj;
DELIMITER $$
CREATE PROCEDURE delete_xs(IN xh char(6))
-- IN:输入参数,调用时必须提供一个值,过程中只读。

-- xh char(6):参数名为 xh,固定长度字符串,最多 6 个字符(对应学号字段类型)。
BEGIN
	DELETE FROM xs WHERE学号=xh;
	SELECT * FROM xs;
END $$
DELIMITER ;

存储过程的调用

sql 复制代码
CALL delete_xs('221101');

删除存储过程

sql 复制代码
DROP PROCEDURE [IF EXISTS] 过程名;

查看存储过程

sql 复制代码
-- 查看所有存储过程状态
SHOW PROCEDURE STATUS;

-- 查看指定数据库的存储过程
SHOW PROCEDURE STATUS WHERE Db = '数据库名';

-- 查看创建语句
SHOW CREATE PROCEDURE 过程名;

存储过程体

sql 复制代码
CREATE PROCEDURE 过程名(参数列表)
BEGIN
    -- 这里就是存储过程体
    -- 可以包含变量声明、条件判断、循环、游标、错误处理等
END

变量声明

sql 复制代码
DECLARE 变量名 数据类型 [DEFAULT 默认值];
sql 复制代码
DECLARE total INT DEFAULT 0;
DECLARE student_name VARCHAR(50);

变量赋值

sql 复制代码
SET 变量名 = 值;
-- 或者
SELECT 列名 INTO 变量名 FROM 表 WHERE 条件;

通过变量显示xs表中指定学号学生姓名和专业名的值(分别赋给变量)。

sql 复制代码
DELIMITER $$
CREATE PROCEDURE xscj.xs_vdisp
	(IN xhchar(6) )
BEGIN
DECLARE name, project varchar(10);
SELECT 姓名,专业INTO name, project 
	FROM xs
	WHERE 学号= xh;
SELECT name, project;
END$$
DELIMITER ;
 
CALL xscj.xs_vdisp('221101');

条件判断

if语句

sql 复制代码
IF 条件 THEN
    语句;
ELSEIF 条件 THEN
    语句;
ELSE
    语句;
END IF;

创建存储过程,比较第1个参数和第2个参数整数大小,显示比较结果。

sql 复制代码
DELIMITER $$
CREATE PROCEDURE xscj.intComp
		(IN k1 integer, IN k2 integer, OUT k3 char(6) )
BEGIN
	IF k1>k2 THEN
		SET k3= '大于';
	ELSEIF k1=k2 THEN
		SET k3= '等于';
	ELSE 
		SET k3= '小于';
	END IF;
END$$
DELIMITER ;
 
CALL xscj.intComp(12,46, @k3);
SELECT @k3;

case语句

sql 复制代码
CASE 表达式
    WHEN 值1 THEN 语句;
    WHEN 值2 THEN 语句;
    ELSE 语句;
END CASE;

创建一个存储过程,把百分制变成等级。

sql 复制代码
DELIMITER $$
CREATE PROCEDURE xscj.cjGrade
	(IN cjtinyint(1), OUT grade varchar(4) )
BEGIN
CASE 
	WHEN cj>=90 THEN SETgrade='优秀';
	WHEN cj>=80 AND cj<90 THEN SET grade ='良好';
	WHEN cj>=70 AND cj<80 THEN SET grade ='中等';
	WHEN cj>=60 AND cj<70 THEN SET grade ='及格';
	ELSE  SET grade ='不及格';
	SELECT grade;
END CASE;
END$$
DELIMITER ;
 
CALL xscj.cjGrade(85, @gcj);
SELECT @gcj;	

while循环(先判断后执行)

sql 复制代码
WHILE 条件 DO
    语句;
END WHILE;

采用WHILE计算1+2+3+...+n。

sql 复制代码
USE xscj;
DROP PROCEDURE IF EXISTS proc_nsum;
CREATE PROCEDURE proc_nsum()
BEGIN
	DECLARE n, s int DEFAULT 0;
	WHILE n <= @n DO
		SET s = s + n;
		SET n = n + 1;
	END WHILE;
	SELECT '1+2+3+...+',@n,'=',s;
END ;
 
SET @n = 50;
CALL proc_nsum();

REPEAT(先执行后判断,至少执行一次)

sql 复制代码
REPEAT
    语句;
UNTIL 条件 END REPEAT;

存储函数

创建

sql 复制代码
CREATE FUNCTION 函数名(参数列表)
RETURNS 返回数据类型
[DETERMINISTIC | NOT DETERMINISTIC]
[CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA]
BEGIN
    -- 函数体
    RETURN 值;
END

调用

sql 复制代码
-- 直接调用
SELECT 函数名(参数);

-- 在表达式中使用
SELECT 姓名, 函数名(参数) AS 自定义列 FROM 表;
WHERE 函数名(列) > 值;
SET @变量 = 函数名(参数);

查看与删除

sql 复制代码
-- 查看所有函数状态
SHOW FUNCTION STATUS;
-- 筛选指定库
SHOW FUNCTION STATUS WHERE Db = 'xscj';

-- 查看创建定义
SHOW CREATE FUNCTION 函数名;

-- 删除
DROP FUNCTION IF EXISTS 函数名;

创建一个存储函数,把百分制变成等级。

sql 复制代码
USE xscj;
DROP FUNCTION IF EXISTS xscj.cjGradeFun;
DELIMITER $$
CREATE FUNCTION xscj.cjGradeFun(cj tinyint(1))
RETURNS char(3)
BEGIN
	DECLARE grade char(3);
	CASE 
		WHEN cj>=90 THEN SET grade='优秀';
		WHEN cj>=80 AND cj<90 THEN SET grade='良好';
		WHEN cj>=70 AND cj<80 THEN SET grade='中等';
		WHEN cj>=60 AND cj<70 THEN SET grade='及格';
		ELSE  SET grade='不及格';
	END CASE;
		RETURN grade;
END$$
DELIMITER ;
 
SET @cj=85;
SELECT @cj,xscj.cjGradeFun(@cj);

根据学号返回姓名

sql 复制代码
DELIMITER //
CREATE FUNCTION get_student_name(sid CHAR(6))
RETURNS VARCHAR(50)
DETERMINISTIC READS SQL DATA
BEGIN
    DECLARE s_name VARCHAR(50);
    SELECT 姓名 INTO s_name FROM xs WHERE 学号 = sid;
    RETURN IFNULL(s_name, '未知');
END //
DELIMITER ;

-- 调用
SELECT get_student_name('221101');
SELECT 学号, get_student_name(学号) AS 姓名 FROM xs;

计算成绩等级

sql 复制代码
DELIMITER //
CREATE FUNCTION get_grade(score DECIMAL(5,2))
RETURNS VARCHAR(10)
DETERMINISTIC NO SQL
BEGIN
    RETURN CASE
        WHEN score >= 90 THEN '优秀'
        WHEN score >= 80 THEN '良好'
        WHEN score >= 70 THEN '中等'
        WHEN score >= 60 THEN '及格'
        ELSE '不及格'
    END;
END //
DELIMITER ;

-- 调用
SELECT 学号, 课程号, 成绩, get_grade(成绩) AS 等级 FROM cj;

创建一个函数,将手机号中间四位隐藏,如 138****5678。

sql 复制代码
DELIMITER //
CREATE FUNCTION mask_phone(phone CHAR(11))
RETURNS CHAR(11)
DETERMINISTIC NO SQL
BEGIN
    IF phone IS NULL OR LENGTH(phone) != 11 THEN
        RETURN NULL;
    END IF;
    RETURN CONCAT(LEFT(phone, 3), '****', RIGHT(phone, 4));
END //
DELIMITER ;

-- 使用
SELECT 姓名, mask_phone(手机号) AS 手机号 FROM xs;

游标

游标步骤

sql 复制代码
声明游标 DECLARE cursor_name CURSOR FOR SELECT ...

打开游标 OPEN cursor_name

提取数据 FETCH cursor_name INTO variable_list

关闭游标 CLOSE cursor_name

声明

sql 复制代码
DECLARE 游标名 CURSOR FOR SELECT语句
sql 复制代码
DECLARE xs_cur1 CURSOR	FOR
 	SELECT学号,姓名,性别,出生日期,总学分
 		FROM xscj
 		WHERE 专业= '计算机';

创建一个包含游标的存储过程,统计xs表中指定班级人数和获得最大学号。

sql 复制代码
USE xscj;
DELIMITER $$
DROP PROCEDURE IF EXISTS class;
CREATE PROCEDURE class(IN bj char(4),OUT number integer,OUT xhmax char(6))
BEGIN 
	DECLARE xh char(6);
	DECLARE found bit DEFAULT TRUE;
	DECLARE number_xs CURSOR FOR 
		SELECT 学号 FROM xs WHERE LEFT(学号,4)=bj ORDER BY 学号;
	DECLARE CONTINUE HANDLER FOR NOT FOUND
		SET found=false;
	SET number=0;
	OPEN number_xs;
	FETCH number_xs INTO xh;
	WHILE found DO 
		 SET number=number+1;
SET xhmax= xh;
		 FETCH number_xs INTO xh;
	END WHILE;
	CLOSE number_xs;
END $$
DELIMITER ;

调用此存储过程并查看结果:

sql 复制代码
SET @bj='2211';
CALL xscj.class('2211',@num,@xh);
SELECT@bj 班级, @num 人数, @xh 最大学号;

触发器

触发器的类型

触发器按触发时机触发事件组合分类:

时机 事件 说明
BEFORE INSERT 插入前触发
BEFORE UPDATE 更新前触发
BEFORE DELETE 删除前触发
AFTER INSERT 插入后触发
AFTER UPDATE 更新后触发
AFTER DELETE 删除后触发

创建触发器语法

sql 复制代码
CREATE TRIGGER 触发器名
{BEFORE | AFTER} {INSERT | UPDATE | DELETE}
ON 表名
FOR EACH ROW
[FOLLOWS | PRECEDES 其他触发器名]
BEGIN
    -- 触发器体
END;

查看与删除当前数据库中触发器

sql 复制代码
-- 查看所有触发器
SHOW TRIGGERS;

-- 查看特定数据库的触发器
SHOW TRIGGERS FROM xscj;

-- 查看创建语句
SHOW CREATE TRIGGER 触发器名;

-- 删除触发器
DROP TRIGGER IF EXISTS 触发器名;

创建触发器,当向cj表插入一行数据时,根据成绩对xs表的总学分进行修改。如果成绩≥60分,则总学分加上该课程的学分,否则总学分不变。

sql 复制代码
USE xscj;
DROP TRIGGER IF EXISTS cj_insert;
DELIMITER $$
CREATE TRIGGER cj_insert AFTER INSERT
	ON cj FOR EACH ROW
BEGIN
	DECLARE xf int(1);
	SELECT 学分 INTO xf FROM kc WHERE 课程号=NEW.课程号;
	IF new.成绩>=60 THEN
		UPDATE xs SET 总学分=总学分+xf WHERE 学号=NEW.学号;
	END IF;
END$$
DELIMITER;
sql 复制代码
USE xscj;
INSERT INTO xs
	VALUES('221199', '王大庆', '计算机', 1, '2004-08-14', 16,NULL,NULL);
SELECT 学号,姓名,总学分 FROM xs WHERE 学号='221199';					#(a)
INSERT INTO cj(学号,课程号,成绩) VALUES('221199','206',60); 
SELECT 学号,姓名,总学分 FROM xs WHERE 学号='221199';					#(b)

创建一个触发器,当修改cj表中数据时,根据修改前后成绩是否大于等于60,调整xs表总学分。

sql 复制代码
USE xscj;
DROP TRIGGER IF EXISTS cj_update;
DELIMITER $$
CREATE TRIGGER cj_update BEFORE UPDATE
	ON cj FOR EACH ROW
BEGIN
	DECLARE xf int(1);
	SELECT 学分 INTO xf FROM kc WHERE 课程号=NEW.课程号;
	IF OLD.成绩>=60 AND NEW.成绩<60 THEN
		UPDATE xs SET 总学分=总学分-xf WHERE 学号=OLD.学号;
	END IF;
	IF OLD.成绩<60 AND NEW.成绩>=60 THEN
		UPDATE xs SET 总学分=总学分+xf WHERE 学号=OLD.学号;
	END IF;		
END$$
DELIMITER ;
sql 复制代码
USE xscj;
UPDATE cj SET 成绩=50 WHERE 学号='221199';
SELECT 学号,姓名, 总学分 FROM xs WHERE 学号='221199';

创建一个触发器,当删除表xs中某个学生的信息时,同时将cj表中与该学生有关的记录全部删除。

sql 复制代码
USE xscj;
DROP TRIGGER IF EXISTS xs_delete;
DELIMITER $$
CREATE TRIGGER xs_delete AFTER DELETE
	ON xs FOR EACH ROW
BEGIN
	DELETE FROMcjWHERE 学号=OLD.学号;
END$$
DELIMITER ;
sql 复制代码
SELECT * FROM cj WHERE 学号='221199';					#(a)
DELETE FROM xs WHERE 学号='221199';
SELECT * FROM cj WHERE 学号='221199';

综合示例:审计日志自动生成

创建一个完整的触发器体系,记录所有对 xs 表的增删改操作。

sql 复制代码
-- 日志表
CREATE TABLE audit_log (
    audit_id INT AUTO_INCREMENT PRIMARY KEY,
    action CHAR(1),        -- 'I' 插入, 'U' 更新, 'D' 删除
    stu_id CHAR(6),
    old_data JSON,
    new_data JSON,
    changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- INSERT 触发器
DELIMITER //
CREATE TRIGGER tr_xs_insert
AFTER INSERT ON xs
FOR EACH ROW
BEGIN
    INSERT INTO audit_log (action, stu_id, new_data)
    VALUES ('I', NEW.学号, JSON_OBJECT('姓名', NEW.姓名, '性别', NEW.性别, '电话', NEW.电话));
END //

-- UPDATE 触发器
CREATE TRIGGER tr_xs_update
AFTER UPDATE ON xs
FOR EACH ROW
BEGIN
    INSERT INTO audit_log (action, stu_id, old_data, new_data)
    VALUES ('U', NEW.学号,
            JSON_OBJECT('姓名', OLD.姓名, '性别', OLD.性别, '电话', OLD.电话),
            JSON_OBJECT('姓名', NEW.姓名, '性别', NEW.性别, '电话', NEW.电话));
END //

-- DELETE 触发器
CREATE TRIGGER tr_xs_delete
AFTER DELETE ON xs
FOR EACH ROW
BEGIN
    INSERT INTO audit_log (action, stu_id, old_data)
    VALUES ('D', OLD.学号, JSON_OBJECT('姓名', OLD.姓名, '性别', OLD.性别, '电话', OLD.电话));
END //
DELIMITER ;

这样任何对 xs 表的数据变更都会自动记录,便于审计和恢复。

事件

开启调度器

sql 复制代码
SET GLOBAL EVENT_SCHEDULER = TRUE;

2022年11月11日0时0分所有商品单价下调10%。

①创建表

sql 复制代码
USE test;
CREATE TABLE sp
(
	id 		int(2) NOT NULL,
	price	float(6.2)
);
INSERT INTO sp
	VALUES(1,20.50),(2,68.00);
SELECT * FROM sp;

②创建事件

sql 复制代码
USE test;
DROP EVENT IF EXISTS changePrice;
CREATE EVENT changePrice
	ON SCHEDULE AT '2022-11-11'
	DO 
		# 在商品数据库中进行调整价格的操作
		update test.sp SET sp.price= ROUND(0.9*sp.price);
SET GLOBAL EVENT_SCHEDULER = TRUE;

③测试事件

sql 复制代码
USE test;
SELECT * FROM sp;

从2022年开始,每年9月30日将已经毕业的学生表和成绩表记录转移到历史数据库中,至2030年结束。

① 创建与学生(xs)表和成绩(cj)表相同结构的历史表xs_ls和cj_ls。

sql 复制代码
USE xscj;
CREATE TABLE xs_ls LIKE xs;
CREATE TABLE cj_ls LIKE cj;

②准备学生(xs)表和成绩(cj)表拷贝,将进校最早的学生(学号'20xxxx')根据当前系统时间,修改为毕业班学生学号,这样既可以测试事件,又不破坏原表数据记录。

sql 复制代码
USE xscj;
CREATE TABLE xs_copy AS ( SELECT * FROM xs );
CREATE TABLE cj_copy AS ( SELECT * FROM cj );
UPDATE xs_copy SET 学号=CONCAT(@byyear,SUBSTR(学号,3,4)) WHERE LEFT(学号,2)='20';
UPDATE cj_copy SET 学号=CONCAT(@byyear,SUBSTR(学号,3,4)) WHERE LEFT(学号,2)='20';

③创建事件调用的存储过程,实现事件功能。

sql 复制代码
USE xscj;
DELIMITER $$
DROP PROCEDURE IF EXISTS xscj.xscj_lsp;
CREATE PROCEDURE xscj.xscj_lsp( )
	BEGIN
		DECLARE byyear char(2); 
		SET byyear= cast(year(now())-2004 AS char(2));
		INSERT INTO xscj.xs_ls 
			SELECT * FROM xscj.xs_copy WHERE left(学号,2)= byyear;
		INSERT INTO xscj.cj_ls  
			SELECT * FROM xscj.cj_copy WHERE left(学号,2)= byyear;
		DELETE FROM xscj.xs_copy WHERE left(学号,2)= byyear;
		DELETE FROM xscj.cj_copy WHERE left(学号,2)= byyear;
	END$$
DELIMITER ;

④创建事件

sql 复制代码
USE xscj;
DROP EVENT IF EXISTS xscj_tols;
CREATE EVENT xscj_tols
	ON SCHEDULE EVERY 1 YEAR
	STARTS '2022-09-30'
	ENDS '2030-09-30'
	DO 
		CALL xscj.xscj_lsp();
SET GLOBAL EVENT_SCHEDULER = TRUE;

⑤测试事件

sql 复制代码
USE xscj;
SET @byyear= CAST(year(now())-2004 AS char(2));
SELECT * FROM xs_copy WHERE left(学号,2)= @byyear;			#(a)
SELECT * FROM cj_copy WHERE left(学号,2)= @byyear;			#(b)
SELECT * FROM xs_ls WHERE left(学号,2)= @byyear;			#(c)
SELECT * FROM cj_ls WHERE left(学号,2)= @byyear;

修改事件

将事件changePrice的名字改成sp_changePrice。

sql 复制代码
ALTER EVENT changePrice
	RENAME TO spchangePrice; 

删除事件

sql 复制代码
DROP EVENT [IF EXISTS][数据库名.] 事件名

表数据导出

将kc表导出,以.txt格式存储

①确认允许的路径

sql 复制代码
SHOW VARIABLES LIKE 'secure_file_priv';
sql 复制代码
USE xscj;
SELECT * FROM kc
INTO OUTFILE '/var/lib/mysql-files/kc.txt'
FIELDS TERMINATED BY ','          -- 逗号分隔
LINES TERMINATED BY '\n';

表数据导入

创建course表,将txt文件中存储的数据恢复到该表中,并显示过程和结果,截图上传

sql 复制代码
DESC kc;

①创建course表

sql 复制代码
CREATE TABLE course (
    课程号 CHAR(6) PRIMARY KEY,
    课程名 VARCHAR(50),
    学分 INT
);

② 导入

sql 复制代码
LOAD DATA INFILE '/var/lib/mysql-files/kc.txt'
INTO TABLE course
FIELDS TERMINATED BY '\t'          -- 必须与导出时的分隔符一致
ENCLOSED BY '"'                    -- 如果导出时有包裹符
LINES TERMINATED BY '\n';

③验证

sql 复制代码
SELECT * FROM course;

用户管理

添加两个新的用户,king的密码为queen,palo的密码为530415。

sql 复制代码
CREATE USER
    	'king'@'localhost' IDENTIFIED BY 'queen', 
    	'palo'@'localhost' IDENTIFIED BY '530415';

查看用户

sql 复制代码
USE mysql;
SHOW tables;
SELECT * FROM user;

删除用户palo。

sql 复制代码
DROP USER palo@localhost;

修改用户名

sql 复制代码
RENAME USER '旧名'@'host' TO '新名'@'host';

将用户ken的密码修改为qen。

sql 复制代码
SET PASSWORD FOR 'ken'@'localhost' = PASSWORD('qen');

权限控制

给谁? → 能做什么? → 在哪个东西上? → 有没有附加能力?

权限

1. 数据操作权限(表级 / 列级)

权限 说明
SELECT 查询数据
INSERT 插入新数据
UPDATE 修改现有数据
DELETE 删除数据

2. 结构管理权限(表级 / 数据库级)

权限 说明
CREATE 创建数据库或表
DROP 删除数据库或表
ALTER 修改表结构(增删列、改类型)
INDEX 创建 / 删除索引
REFERENCES 创建外键约束

3. 程序对象权限

权限 说明
CREATE VIEW 创建视图
SHOW VIEW 查看视图的定义语句
CREATE ROUTINE 创建存储过程 / 函数
ALTER ROUTINE 修改或删除存储过程 / 函数
EXECUTE 执行存储过程 / 函数
TRIGGER 创建 / 删除触发器
EVENT 管理事件

4. 高级管理权限(通常只给管理员)

权限 说明
ALL [PRIVILEGES] 授予除 GRANT OPTIONPROXY 外的所有权限
SUPER 超级权限(KILL 查询、修改全局参数等)
PROCESS 查看所有进程(SHOW PROCESSLIST
RELOAD 执行 FLUSH 操作
SHOW DATABASES 列出所有数据库(默认权限)
FILE 使用 SELECT INTO OUTFILELOAD_FILE 读写文件
CREATE USER 创建 / 删除 / 修改用户
GRANT OPTION 允许将自己的权限授予其他用户(非常重要)

对象层级:在哪个东西上

权限有精确的作用范围,从大到小依次是:

对象层级 写法 效果
全局 ON *.* 对所有数据库下的所有表生效
数据库 ON 数据库名.* 对指定库下的所有表生效
ON 数据库名.表名 只对那张具体的表生效
ON 数据库名.表名 (列1, 列2) 只对表中指定的几列生效
存储过程/函数 ON PROCEDURE 数据库名.过程名ON FUNCTION 数据库名.函数名 只对该过程/函数生效

示例对比:

sql 复制代码
-- 全局 SELECT:可以查所有库所有表(包括以后新建的库)
GRANT SELECT ON *.* TO 'reader'@'%';

-- 数据库级:只能查 xscj 库里的表,不能碰别的库
GRANT SELECT ON xscj.* TO 'reader2'@'%';

-- 表级:只能查 xscj.xs 这一张表
GRANT SELECT ON xscj.xs TO 'reader3'@'%';

-- 列级:只能查 xs 表中的姓名和学号两列
GRANT SELECT (学号, 姓名) ON xscj.xs TO 'reader4'@'%';

TO 和 FROM

  • TO :授权给某个用户。如果用户不存在,MySQL 5.7 及更早版本会自动创建该用户;MySQL 8.0 则必须先 CREATE USER 再授权(推荐先创建再授权,避免不经意创建空密码用户)。
  • FROM :撤销权限时使用(见 REVOKE)。撤销的语法结构与授权几乎对称,只是把 TO 换成 FROM,并且 GRANT OPTION 需要单独回收。

WITH 选项

在授权语句末尾可以添加附加选项,控制用户权限的传递或资源限制。

1. WITH GRANT OPTION

允许该用户把自己拥有的权限再授给别人。这就像公司里你有了公章使用权,还能给下属盖章的权限。

sql 复制代码
GRANT SELECT ON xscj.* TO 'manager'@'%' WITH GRANT OPTION;

manager 现在可以执行:

sql 复制代码
GRANT SELECT ON xscj.* TO 'clerk'@'%';

危险 :如果给的是 ALL PRIVILEGESGRANT OPTION,那个用户就成了管理员,可以任意授权甚至提升自己权限。必须严格控制。

2. 资源限制

可以限制用户每小时执行的查询、更新、连接次数,防止单个用户过度消耗资源。

sql 复制代码
GRANT SELECT ON xscj.* TO 'batch'@'%'
WITH MAX_QUERIES_PER_HOUR 100          -- 每小时最多执行100次查询
     MAX_UPDATES_PER_HOUR 20            -- 每小时最多更新20次
     MAX_CONNECTIONS_PER_HOUR 5         -- 每小时最多连接5次
     MAX_USER_CONNECTIONS 2;            -- 最多允许2个同时连接

当限制达到时,该用户会被拒绝操作,直到下一个时间窗口。

授权实战场景

场景1:给后端应用一个专用账户

sql 复制代码
-- 先创建用户
CREATE USER 'app'@'10.0.0.5' IDENTIFIED BY 'Str0ng!Pass';
-- 授予对业务库的常规读写权限(不授权改结构)
GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.* TO 'app'@'10.0.0.5';

场景2:给数据分析师只读权限

sql 复制代码
CREATE USER 'analyst'@'%' IDENTIFIED BY 'DataAn@ly1';
GRANT SELECT ON mydb.* TO 'analyst'@'%';

场景3:给临时工只查名字和邮箱

sql 复制代码
GRANT SELECT (name, email) ON mydb.contacts TO 'temp'@'localhost';

场景4:给 DBA 大权限,但要限制远程来源

sql 复制代码
CREATE USER 'dba'@'192.168.1.50' IDENTIFIED BY 'DbaAdmin#2025';
GRANT ALL ON *.* TO 'dba'@'192.168.1.50' WITH GRANT OPTION;

绝不把 ALLGRANT OPTION'%' 主机。

查看已授的权限

sql 复制代码
-- 查看某个用户的所有权限
SHOW GRANTS FOR 'app'@'10.0.0.5';

-- 查看当前登录用户自己的权限
SHOW GRANTS;

授予用户ken对xs表的SELECT权限。

sql 复制代码
USE xscj;
GRANT SELECT
	ON xs
	TO ken@localhost;

授予liu和zhang新用户对xs表的SELECT和UPDATE权限。

sql 复制代码
GRANT SELECT,UPDATE
	ON  xs
	TO  liu@localhost IDENTIFIED BY 'pwd123',
  	zhang@localhost IDENTIFIED BY 'pwd123';

授予用户ken对xs表的姓名列和学号列的UPDATE权限。

sql 复制代码
USE xscj;
GRANT UPDATE(姓名,学号)
	ON  xs
	TO  ken@localhost;

授予用户ken对xscj数据库中的所有表的SELECT权限。

sql 复制代码
GRANT SELECT
	ON  xscj.* 
	TO  ken@localhost;

授予用户ken对xscj数据库的所有数据库权限。

sql 复制代码
USE xscj;
GRANT ALL
	ON  *
	TO  ken@localhost;

授予用户peter对所有数据库中的所有表的CREATE、ALTERT和DROP权限。

sql 复制代码
GRANT CREATE,ALTER,DROP
	ON  *.*
	TO  peter@localhost IDENTIFIED BY 'p123';

授予用户peter创建新用户的权限。

sql 复制代码
GRANT CREATE  USER
	ON  *.*
	TO  peter@localhost;

回收用户caddy对xs表的SELECT权限。

sql 复制代码
USE xscj;
REVOKE SELECT
	ON  xs
	FROM  caddy@localhost;

回收用户转授权限的能力

sql 复制代码
REVOKE GRANT OPTION ON 对象层级 FROM '用户名'@'主机';REVOKE GRANT OPTION ON 对象层级 FROM '用户名'@'主机';