MySQL 学习笔记:DCL、函数与约束 ------ 安全、效率、完整性的三板斧
-
- 一、DCL:数据库的 "门禁系统"
-
- [1.1 管理用户:用户 = 用户名 @ 主机名](#1.1 管理用户:用户 = 用户名 @ 主机名)
- [1.2 权限控制:能干什么,细到 "库。表"](#1.2 权限控制:能干什么,细到 "库。表")
- 二、函数:把计算交给数据库
-
- [2.1 字符串函数](#2.1 字符串函数)
- [2.2 数值函数](#2.2 数值函数)
- [2.3 日期函数](#2.3 日期函数)
- [2.4 流程函数](#2.4 流程函数)
- 三、约束:数据完整性的守门员
-
- [3.1 六种约束总览](#3.1 六种约束总览)
- [3.2 一个建表演示](#3.2 一个建表演示)
- [3.3 外键约束:让两张表 "拉上关系"](#3.3 外键约束:让两张表 "拉上关系")
- 四、写在最后
学完 DML 的增删改、DQL 的查询之后,昨天把 MySQL 基础篇剩下的三块硬骨头啃完了: DCL(数据控制语言)、函数、约束 。这三块内容看起来零散,其实各守一条主线:
- DCL → 安全:谁能连我的数据库?连上能干什么?
- 函数 → 效率:把计算交给数据库,而不是拿回程序里算;
- 约束 → 正确性:从源头拦住脏数据,保证数据完整。
这篇笔记把三块内容串起来讲,语法 + 案例 + 我的理解,希望能帮到同样在学 MySQL 的朋友。
一、DCL:数据库的 "门禁系统"
DCL 全称 Data Control Language(数据控制语言),用来管理数据库用户、控制访问权限。这类 SQL 开发人员用得少,主要是 DBA(数据库管理员)在操作,但理解它对你理解 "数据库安全" 很有帮助。
1.1 管理用户:用户 = 用户名 @ 主机名
在 MySQL 中,一个用户不是只有 "用户名",而是由用户名 + 主机名共同唯一标识:
sql
-- 查询所有用户(用户信息存放在 mysql 库的 user 表)
SELECT * FROM mysql.user;
-- 创建用户:'itcast' 只能在本机(localhost)访问
CREATE USER 'itcast'@'localhost' IDENTIFIED BY '123456';
-- 创建用户:'heima' 可以在任意主机(%)访问
CREATE USER 'heima'@'%' IDENTIFIED BY '123456';
-- 修改密码(mysql_native_password 是认证插件)
ALTER USER 'heima'@'%' IDENTIFIED WITH mysql_native_password BY '1234';
-- 删除用户
DROP USER 'itcast'@'localhost';
关键点:
Host表示该用户允许从哪台主机访问:localhost只允许本机,%是通配符,允许任意主机;- 所以
'itcast'@'localhost'和'itcast'@'%'是两个不同的用户; - 这类操作主要面向 DBA,开发人员平时接触不多。
1.2 权限控制:能干什么,细到 "库。表"
sql
-- 查询某用户的权限
SHOW GRANTS FOR 'heima'@'%';
-- 授予权限:授予 heima 操作 itcast 库所有表的所有权限
GRANT ALL ON itcast.* TO 'heima'@'%';
-- 撤销权限
REVOKE ALL ON itcast.* FROM 'heima'@'%';
常见权限有:ALL / ALL PRIVILEGES(所有)、SELECT、INSERT、UPDATE、DELETE、ALTER、DROP、CREATE 等。
关键点:
- 多个权限用逗号分隔:
GRANT SELECT, INSERT, UPDATE ON itcast.* TO ...; 数据库名.表名支持*通配:itcast.*表示 itcast 库的所有表,*.*表示所有库的所有表;- 权限粒度可以精确到 "某个库的某张表",这就是 "最小权限" 的基础。
最小权限原则 是数据库安全的第一课:给用户只授 "够用" 的权限。业务账号通常只需要 SELECT/INSERT/UPDATE/DELETE,结构变更(ALTER/DROP)留给 DBA 账号。别图省事直接 GRANT ALL。
Host 就是安全边界 :'xxx'@'%' 等于把数据库暴露给任意 IP。生产环境如果业务账号用了 %,风险相当大 ------ 最好精确到应用服务器所在的网段或具体 IP。
生产环境不要用 root 跑业务:root 权限无法精细限制,出了问题也没有追溯手段。这也是 "最小权限账号" 存在的意义。
补充两点课件没细说的:
- MySQL 8 默认认证插件是
caching_sha2_password,老版本客户端连不上时,才需要改成mysql_native_password; - 老版本手册里经常出现
FLUSH PRIVILEGES,现在GRANT/REVOKE会立即生效,一般不需要手动刷新。
二、函数:把计算交给数据库
函数是 MySQL 内置好的一段可调用代码,业务里直接调用即可。课件用两个场景引入:算入职天数 (用 DATEDIFF)、判分数等级(用 CASE WHEN)。MySQL 函数分四类:字符串、数值、日期、流程。
2.1 字符串函数
| 函数 | 功能 |
|---|---|
CONCAT(S1,S2,...) |
字符串拼接 |
LOWER(str) / UPPER(str) |
全部转小写 / 转大写 |
LPAD(str,n,pad) / RPAD(str,n,pad) |
左 / 右填充到 n 个字符 |
TRIM(str) |
去掉首尾空格 |
SUBSTRING(str,start,len) |
从 start 起截取 len 个字符 |
经典案例 ------ 工号补零:业务变更要求工号统一为 5 位,不足前面补 0:
sql
UPDATE emp SET workno = LPAD(workno, 5, '0');
-- 1号员工工号 1 → 00001
这个场景在真实业务里非常常见:订单号、学号、流水号要统一长度,LPAD/RPAD 就是干这个的。
2.2 数值函数
| 函数 | 功能 |
|---|---|
CEIL(x) |
向上取整 |
FLOOR(x) |
向下取整 |
MOD(x,y) |
x/y 的模(余数) |
RAND() |
返回 0~1 随机数 |
ROUND(x,y) |
四舍五入,保留 y 位小数 |
经典案例 ------6 位随机验证码:
sql
SELECT LPAD(ROUND(RAND() * 1000000, 0), 6, '0');
思路拆解:RAND() 得到 0~1 随机数 → 乘 1000000 放大 → ROUND(...,0) 取整去掉小数 → LPAD(...,6,'0') 不足 6 位补零。三个函数嵌套在一起,从里往外读,逻辑很清晰。
2.3 日期函数
| 函数 | 功能 |
|---|---|
CURDATE() / CURTIME() / NOW() |
当前日期 / 当前时间 / 当前日期时间 |
YEAR(date) / MONTH(date) / DAY(date) |
提取年 / 月 / 日 |
DATE_ADD(date, INTERVAL expr type) |
日期加指定时间间隔 |
DATEDIFF(date1, date2) |
两个日期相差天数 |
经典案例 ------ 入职天数:查询所有员工的入职天数,按入职天数倒序:
sql
SELECT name, DATEDIFF(CURDATE(), entrydate) AS 'entrydays'
FROM emp
ORDER BY entrydays DESC;
2.4 流程函数
| 函数 | 功能 |
|---|---|
IF(value, t, f) |
value 为真返回 t,否则返回 f |
IFNULL(value1, value2) |
value1 非空返回它,否则返回 value2 |
CASE WHEN 条件 THEN 结果 ... ELSE 默认 END |
条件判断(可多分支) |
CASE 表达式 WHEN 值 THEN 结果 ... END |
等值判断 |
经典案例 ------ 工作地址分级:北京 / 上海 → 一线城市,其他 → 二线城市:
sql
SELECT name,
(CASE workaddress WHEN '北京' THEN '一线城市'
WHEN '上海' THEN '一线城市'
ELSE '二线城市' END) AS '工作地址'
FROM emp;
经典案例 ------ 成绩分档 :≥85 优秀、≥60 及格、否则不及格(注意这里用的是 CASE WHEN 条件式写法,因为要做范围判断):
sql
SELECT id, name,
(CASE WHEN math >= 85 THEN '优秀'
WHEN math >= 60 THEN '及格'
ELSE '不及格' END) AS '数学',
(CASE WHEN english >= 85 THEN '优秀'
WHEN english >= 60 THEN '及格'
ELSE '不及格' END) AS '英语'
FROM score;
单行函数 vs 聚合函数 :今天学的都是单行函数 (对一行数据计算,返回一行结果);之前学的 COUNT/AVG/MAX 是聚合函数(多行压成一行)。搞清楚这个分类,读 SQL 心里更有数。
函数可以嵌套:验证码案例就是三层嵌套。嵌套函数的读法是 "从里往外"------ 最里层先算,结果作为外层参数。
几个易错点:
SUBSTRING的起始位置从 1 开始 ,SUBSTRING('Hello MySQL', 1, 5)结果是Hello;TRIM只去首尾空格,不去中间空格;IFNULL只认 NULL:IFNULL('', 'Default')返回''(空字符串不是 NULL,不会走默认值);- 两种
CASE写法别混用:CASE 字段 WHEN 值只能做等值判断,范围判断必须用CASE WHEN 条件。
补充常用函数 (课件没列但很常用):CHAR_LENGTH(字符数)、REPLACE(替换)、DATE_FORMAT(日期格式化)、TIMESTAMPDIFF(更灵活的时间差)、LAST_DAY(当月最后一天)。
一个进阶提醒 :在 WHERE 里对字段套函数(如 WHERE YEAR(entrydate) = 2020)会导致该字段的索引失效、全表扫描。函数不是随便放哪都行,"函数用在哪" 是 SQL 优化的经典考点,进阶篇会展开。
三、约束:数据完整性的守门员
概念:约束是作用于表中字段上的规则 ,用于限制存储的数据。目的:保证数据库中数据的正确性、有效性和完整性。
3.1 六种约束总览
| 约束 | 描述 | 关键字 |
|---|---|---|
| 非空约束 | 限制字段不能为 NULL | NOT NULL |
| 唯一约束 | 保证字段值唯一、不重复 | UNIQUE |
| 主键约束 | 一行数据的唯一标识,非空且唯一 | PRIMARY KEY |
| 默认约束 | 未指定值时采用默认值 | DEFAULT |
| 检查约束(8.0.16+) | 保证字段值满足某个条件 | CHECK |
| 外键约束 | 让两张表建立连接,保证一致性 | FOREIGN KEY |
约束可以在创建表 或修改表时添加。
3.2 一个建表演示
sql
CREATE TABLE tb_user(
id int AUTO_INCREMENT PRIMARY KEY COMMENT 'ID唯一标识',
name varchar(10) NOT NULL UNIQUE COMMENT '姓名',
age int CHECK (age > 0 && age <= 120) COMMENT '年龄',
status char(1) DEFAULT '1' COMMENT '状态',
gender char(1) COMMENT '性别'
);
验证约束是否生效:
- 插
name = NULL→ 违反NOT NULL,报错; - 插重复
name→ 违反UNIQUE,报错; - 插
age = -1或age = 121→ 违反CHECK,报错; - 不填
status→ 自动变成默认值'1'(DEFAULT生效)。
3.3 外键约束:让两张表 "拉上关系"
外键 用来让两张表的数据之间建立连接,从而保证数据的一致性和完整性。典型场景:员工表 emp.dept_id 关联部门表 dept.id。
课件里做了一个很好的实验:不建外键时 ,直接删除 dept 表 id=1 的部门会成功,但 emp 表里还有一堆员工挂着 dept_id=1------ 数据 "悬空" 了,这就是不一致。外键就是来解决这个问题的。
sql
-- 建表时添加外键
[CONSTRAINT 外键名] FOREIGN KEY (外键字段) REFERENCES 主表(主键列);
-- 给已有表添加外键(emp.dept_id → dept.id)
ALTER TABLE emp ADD CONSTRAINT fk_emp_dept_id
FOREIGN KEY (dept_id) REFERENCES dept(id);
-- 删除外键
ALTER TABLE emp DROP FOREIGN KEY fk_emp_dept_id;
加了外键之后,再删除 dept 里被引用的记录会直接报错------ 外键生效了。
删除 / 更新行为:父表记录被删 / 改时,子表怎么办?有四种选择(配图见下):
| 行为 | 效果 |
|---|---|
NO ACTION / RESTRICT |
子表有引用就拒绝删除 / 更新(默认,两者一致) |
CASCADE |
子表级联删除 / 更新 |
SET NULL |
子表外键列置为 NULL(要求外键允许 NULL) |
SET DEFAULT |
置为默认值(InnoDB 不支持) |
sql
-- 级联示例:父表更新/删除时,子表跟着变
ALTER TABLE emp ADD CONSTRAINT fk_emp_dept_id
FOREIGN KEY (dept_id) REFERENCES dept(id)
ON UPDATE CASCADE ON DELETE CASCADE;
约束的本质是把规则交给数据库。没有约束时,数据质量只能靠应用代码 "自觉";一旦换入口(DBA 直接改库、脚本批量导入),脏数据就进来了。约束是数据库层面的守门员,从源头拦截。
主键 vs 唯一约束:
- 主键 = 非空 + 唯一,一张表只能有一个主键;
- 唯一约束可以有多个,而且唯一约束允许多个 NULL(NULL 之间不算重复);
- 两者都会自动创建索引(这也是 "约束顺便建索引" 的知识点)。
CHECK 约束的版本坑 :CHECK 语法在 MySQL 5.7 就能写,但 8.0.16 之前会被静默忽略------ 不报错、不生效。所以别以为写了 CHECK 就万事大吉,先确认版本。
外键 vs 逻辑外键(面试高频):
- 用外键:一致性由数据库强制保证,安全;
- 不用外键:每次增删改都要检查关联,有性能开销;父表删除受限,业务不灵活;表间强耦合。
- 现实里很多互联网公司用 "逻辑外键"------ 表里存
dept_id,但不在数据库建外键,由应用层保证一致性。我的观点:数据一致性要求高、写并发低的系统用外键;高并发互联网场景常用逻辑外键 + 应用层校验。面试时能说出这个取舍,比单纯背定义强得多。
CASCADE 是双刃剑 :级联删除确实方便,但 DELETE FROM dept WHERE id=1 可能连带删掉几百条员工记录,删错不可逆。生产环境对级联要非常谨慎。课件也提醒:业务系统中一般不会修改主键值。
四、写在最后
至此,MySQL 基础篇的 SQL 五类语言(DDL、DML、DQL、DCL、TCL)已经学完四类,下一站是多表查询和事务。
练习建议 :把 tb_user 建出来亲手试一遍约束;用 emp 表分别实现 "工号补零"" 入职天数 ""工作地址分级" 三个案例;再试试给两张表加外键,观察 CASCADE 和 SET NULL 的区别 ------ 动手跑一遍,比看十遍笔记记得牢。
如果这篇笔记对你有帮助,欢迎收藏交流。下一篇见。