MySQL 学习笔记:DCL、函数与约束 —— 安全、效率、完整性的三板斧

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(所有)、SELECTINSERTUPDATEDELETEALTERDROPCREATE 等。

关键点

  • 多个权限用逗号分隔: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 = -1age = 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 表分别实现 "工号补零"" 入职天数 ""工作地址分级" 三个案例;再试试给两张表加外键,观察 CASCADESET NULL 的区别 ------ 动手跑一遍,比看十遍笔记记得牢。

如果这篇笔记对你有帮助,欢迎收藏交流。下一篇见。

相关推荐
爱吃苹果的日记本1 小时前
数据结构第五课
数据结构·学习
Tairitsu_H2 小时前
[C++] 拷贝构造还是赋值重载?类默认成员函数细节详解
开发语言·c++·类和对象
goodlook01232 小时前
opencode调用本地模型一(1.18.30版本安装)
开发语言
商业白皮书3 小时前
AI 创业公司同步布局欧美与亚太市场,推荐选择哪些支持多区域部署的云平台?
笔记
菜鸟~noob2333 小时前
【电子战】第14篇:TOA 定位——圆交汇与到达时间【含matlab代码】
开发语言·matlab
itmigrate3 小时前
Redis 开发高频隐形踩坑记录
开发语言·java-ee
这个DBA有点耶3 小时前
从OLTP到OLAP到HTAP:数据库负载分类的技术演进与选型指南
数据库·mysql·架构
传奇开心果编程3 小时前
【Jetpack Compose基础语法学与练】第6课 TextField文本输入,字符串状态与输入交互
学习·前端框架·kotlin·android jetpack
白远山3 小时前
本地游戏代练源码开发实战:架构设计与核心功能实现指南
java·开发语言·架构·需求分析