一、了解MySQL
1.简介
MySQL 是目前全球最主流的 开源 关系型 数据库管理系统(RDBMS),也是 Java 后端开发的标配组件
通俗理解:它就是一个专门用来「永久存数据、高效管数据」的专业软件。你可以把它想象成一个超级加强版的 Excel 表格仓库:
- 数据按「表格」组织,每张表有固定的列(字段,比如姓名、手机号),每一行是一条完整数据
- 支持用标准化的 SQL 语言,对数据进行增、删、改、查
- 能承载百万、千万级数据,支持成百上千人同时操作,还能保证数据安全、不丢失
其中SQL(Structure Quer Language)是一种操控关系型数据库的语言,几乎所有关系型数据库都支持SQL
所以如果想用好并理解MySQL,最好//还是先学习SQL基础语言(下面是我的SQL笔记)
SQL语言基础:关系型数据库的通用语言(超详细版,看这一篇就够了!)-CSDN博客
(当然,由于Navicat等可视化工具的出现,其实不了解原始代码也可以操作数据库,不过在Java中更改数据库还是需要用到SQL语言哦)
2.能解决什么问题?
它最核心的价值是数据持久化。 你写的 Java 程序运行时,所有数据都暂存在内存里,程序一关闭、电脑一重启,数据就会全部消失。 而 MySQL 会把数据写入硬盘永久保存,程序下次启动还能读取到历史数据。
除此之外它还承担这些核心能力:
3.为什么选择MySQL?
几乎所有需要持久化存储数据的软件系统,都可以用 MySQL:
- 网站、小程序、APP 的后端数据存储(电商、外卖、校园系统、社交平台)
- 企业内部管理系统(OA 办公、CRM 客户管理、ERP 进销存)
- 博客、论坛、内容管理系统
- 数据后台、报表统计、数据分析系统
因为它开源免费、生态成熟、Java 支持度极高,所以是中小项目和互联网公司的首选数据库。
4.用户权限说明
MySQL 权限按作用范围从大到小分为 4 个层级:全局层级 > 数据库层级 > 表层级 > 列层级,高层级权限会自动覆盖低层级范围。以下是开发与运维中最常用的权限分类明细:
(1)数据操作类权限(业务开发核心权限)
这类权限用于控制数据的增删改查,是普通业务用户最常用的权限。
表格
| SELECT | 查询数据,读取表中字段内容 | 全局、库、表、列 |
| INSERT | 向表中插入新增数据行 | 全局、库、表、列 |
| UPDATE | 修改表中已有的数据内容 | 全局、库、表、列 |
| DELETE | 删除表中的数据行 | 全局、库、表 |
(2)结构操作类权限(DDL 权限)
这类权限用于控制数据库、表、索引等结构的创建与修改,一般仅授予开发或运维账号,业务读写账号不建议开放。
表格
| CREATE | 创建新的数据库、数据表 | 全局、库、表 |
| DROP | 删除数据库、数据表、视图 | 全局、库、表 |
| ALTER | 修改表结构(增减字段、调整字段类型等) | 全局、库、表 |
| INDEX | 创建、删除、修改表索引 | 全局、库、表 |
| CREATE VIEW | 创建视图 | 全局、库、表 |
| SHOW VIEW | 查看视图的定义语句 | 全局、库、表 |
| CREATE ROUTINE | 创建存储过程、函数 | 全局、库 |
| ALTER ROUTINE | 修改、删除存储过程、函数 | 全局、库 |
| EXECUTE | 执行存储过程、函数 | 全局、库、表 |
(3)管理类权限(仅管理员账号使用)
这类权限涉及数据库服务的全局管理,生产环境严禁授予普通业务用户。
表格
| ALL PRIVILEGES | 授予除 GRANT OPTION 外的所有常规权限,可简写为 ALL | 全部层级 |
| GRANT OPTION | 允许将自身拥有的权限授予其他用户,或回收其他用户的权限 | 全部层级 |
| CREATE USER | 创建、删除、重命名用户账号 | 全局 |
| SHOW DATABASES | 查看服务器上所有数据库的列表 | 全局 |
| PROCESS | 查看所有用户的数据库连接进程,可终止会话 | 全局 |
| SUPER | 超级管理权限,可修改全局参数、终止任意进程等 | 全局 |
| FILE | 读写数据库服务器本地的文件(用于数据导入导出) | 全局 |
| RELOAD | 执行 FLUSH 类命令,刷新权限、日志、缓存等 | 全局 |
| SHUTDOWN | 关闭数据库服务 | 全局 |
| REPLICATION SLAVE | 从库复制账号专用权限,用于主从数据同步 | 全局 |
(4)基础与特殊权限
表格
| USAGE | 无任何业务权限,仅允许登录数据库。新建用户默认只有此权限 | 全局 |
| REFERENCES | 创建外键约束的权限 | 全局、库、表、列 |
补充说明
权限生效范围示例
- *.* 代表全局层级:所有数据库的所有表
- test_db.* 代表库层级:test_db 库下的所有表
- test_db.emp 代表表层级:test_db 库下的 emp 表
- 列层级写法示例:GRANT SELECT(emp_id, emp_name) ON test_db.emp TO …,仅允许查看指定列
ALL PRIVILEGES 注意点 它不等于 root 账号的全部权限,仅包含常规的读写、结构操作权限,不包含 GRANT OPTION、SUPER 等超级管理权限。
最小权限原则 业务账号只授予必需的 SELECT、INSERT、UPDATE、DELETE 即可,严禁随意开放 ALL PRIVILEGES 或全局权限。
二、实践
1.安装配置
(1)下载数据库
首先到官网(MySQL)下载数据库
如果下载的是压缩包免安装版本,需要进行解压操作:
首先将压缩包解压到纯英文、无空格、无特殊字符的路径下,例如:
D:\\mysql-8.0.39-winx64
❌ 错误路径示例:C:\\Program Files\\mysql(有空格)、D:\\软件\\mysql(有中文)
解压完成后,根目录下会有 bin、docs、include 等文件夹,没有 data 文件夹(后面初始化会自动生成)
(2)编写配置文件
接着手动编写配置文件my.ini (MySQL 启动时会读取这个配置文件,用来指定 安装路径、数据存储路径、端口、字符集 等核心参数)
- 在 MySQL 解压的根目录(和 bin 文件夹同级),新建一个文本文档,重命名为 my.ini(注意后缀名从 .txt 改成 .ini)
- 用记事本打开,粘贴以下基础配置,必须把路径改成你自己的解压路径:
[mysqld]
# MySQL 解压根目录,路径用双反斜杠 \\\\ 或正斜杠 / 都可以
basedir=D:\\\\mysql-8.0.39-winx64
# 数据库数据存放目录,会自动生成,不要手动新建
datadir=D:\\\\mysql-8.0.39-winx64\\\\data
# 服务监听端口,默认3306
port=3306
# 允许的最大连接数
max_connections=200
# 服务端默认字符集,推荐utf8mb4(支持完整emoji)
character-set-server=utf8mb4
# 默认存储引擎
default-storage-engine=INNODB
[mysql]
# 客户端默认字符集
default-character-set=utf8mb4
- 保存文件,编码保持默认 ANSI 即可,不要存成带 BOM 的 UTF-8,否则会识别失败。
(3)配置环境变量(可选)
配置完以后可以选择配置系统环境变量,配置后可以在任意位置打开 CMD 执行 mysql 命令,不用每次都进入 bin 目录(也可以选择跳过这步)
操作步骤:
- 右键「此电脑」→「属性」→「高级系统设置」→「环境变量」
- 在下方「系统变量」里找到 Path,双击编辑
- 点击「新建」,填入你的 MySQL 的 bin 目录路径
(4)初始化数据库
然后就可以初始化数据库了(这一步是 ZIP 版独有的,用来生成数据目录和 root 账号的初始密码,全程必须用管理员身份执行)
-
按下 Win 键,搜索 cmd,右键「命令提示符」→以管理员身份运行
-
如果你配置了环境变量,直接执行命令;没配置的话,先 cd /d D:\\mysql-8.0.39-winx64\\bin 进入 bin 目录
- 执行初始化命令:
mysqld –initialize –console
- –initialize:执行初始化,自动创建 data 目录、系统表、root 账号
- –console:把初始化日志打印在控制台,初始密码会显示在这里,一定要记下来
- 执行完成后,在控制台最后几行找到类似下面的内容,冒号后面的就是 root 用户的临时初始密码,复制保存好:
A temporary password is generated for root@localhost: xxxxxxxx
(注意:如果初始化报错,先把自动生成的 data 文件夹整个删掉,检查 my.ini 里的路径是否正确、路径有没有中文空格,修正后重新执行初始化命令)
(5)注册(卸载)系统服务
接下来注册为Windows系统服务,
注册后就可以像安装版一样,通过服务面板或命令一键启停,还能设置开机自启。
保持管理员 CMD 窗口,执行命令:
mysqld –install MySQL80
- MySQL80 是自定义的服务名,可以自己改,后面启停都用这个名字
- 执行成功会提示 Service successfully installed.
如果以后想卸载服务,执行:
mysqld –remove MySQL80
(6)启动服务并修改密码
1)启动MySQL服务
net start MySQL80
执行成功会提示「MySQL80 服务已经启动成功」,此时数据库就已经在后台运行了
2)登陆并修改初始密码
初始随机密码只能用来第一次登录,必须修改成自己的密码才能正常使用。
- 执行登录命令,回车后输入刚才保存的临时密码:
mysql -u root -p
- 成功进入 MySQL 命令行后,执行修改密码命令(把 你的新密码 换成你自己的密码):
ALTER USER 'root'@'localhost' IDENTIFIED BY '你的新密码';
- 执行完成后,输入 quit 退出,用新密码重新登录一次,验证修改成功。
(7)日常使用
配置完成后,就不用再做初始化、注册服务这些操作了,日常启停和安装版完全一致(二选一):
- 图形化:Win+R 输入 services.msc,找到 MySQL80 服务右键启停
- 命令行(管理员 CMD):
net start MySQL80 # 启动
net stop MySQL80 # 停止
配置完成后,项目就可以通过 localhost:3306,用 root 账号和设置的密码连接本地数据库了
2.连接MySQL
1)使用MySQL自带的客户端连接
在命令提示符中执行上面的登录命令后输入密码即可,show databases; 可以查看数据库
这种方式不怎么方便
2)使用Navicat工具连接MySQL
首先先去官网下载 Navicat | 免费下载 Navicat Premium Lite (这是一个轻量免费版)
然后点击新建连接,自定义名字后输入密码,点击连接测试,如果显示成功,点击确定即可创建
双击左侧列表连接名会自动激活链接(展开列表是库)

(这里主要进行基础语句讲解,可视化软件用法后面可能会单开一章讲解,其实也很简单没啥可讲的,毕竟都可视化了)
3.函数
tips.MySQL再进行字符串查询工作时会自动忽略字符的大小写区别(默认状态下,除非在创建表时加入了binary关键字)
(1)单行函数
1)字符函数
示例基于 utf8mb4 字符集,select语句使用
| 拼接与长度 | CONCAT() | CONCAT(str1, str2, …) | 拼接多个字符串为一个字符串;任意一个参数为 NULL,返回结果就是 NULL | CONCAT('姓名:', '张三') → 姓名:张三 |
| CONCAT_WS() | CONCAT_WS(分隔符, str1, str2, …) | 带分隔符拼接字符串;分隔符不能为 NULL,值为 NULL 的参数会被自动忽略 | CONCAT_WS('-', '2025', '08', '10') → 2025-08-10 | |
| CHAR_LENGTH() | CHAR_LENGTH(str) | 返回字符串的字符个数,中文、英文、数字均算 1 个字符 | CHAR_LENGTH('你好abc') → 5 | |
| LENGTH() | LENGTH(str) | 返回字符串的字节长度;utf8mb4 下常用汉字占 3 字节、英文 / 数字占 1 字节 | LENGTH('你好abc') → 9 | |
| 字符串截取 | SUBSTRING() / SUBSTR() | SUBSTRING(str, 起始位置)SUBSTRING(str, 起始位置, 长度) | 截取字符串子串;位置从 1 开始计数;不写长度则截取到末尾 | SUBSTRING('abcdef', 2, 3) → bcd |
| LEFT() | LEFT(str, n) | 从字符串左侧截取 n 个字符 | LEFT('hello', 3) → hel | |
| RIGHT() | RIGHT(str, n) | 从字符串右侧截取 n 个字符 | RIGHT('hello', 2) → lo | |
| 大小写转换 | UPPER() / UCASE() | UPPER(str) | 将字符串全部转为大写 | UPPER('hello') → HELLO |
| LOWER() / LCASE() | LOWER(str) | 将字符串全部转为小写 | LOWER('HELLO') → hello | |
| 空格与填充 | TRIM() | TRIM(str)TRIM(要移除的字符 FROM str) | 去除字符串首尾空格;也可指定去除首尾的特定字符 | TRIM(' abc ') → abc |
| LTRIM() | LTRIM(str) | 仅去除字符串左侧空格 | LTRIM(' abc') → abc | |
| RTRIM() | RTRIM(str) | 仅去除字符串右侧空格 | RTRIM('abc ') → abc | |
| LPAD() | LPAD(str, 总长度, 填充字符) | 左侧填充字符,使字符串达到指定总长度,常用于补零对齐 | LPAD('123', 5, '0') → 00123 | |
| RPAD() | RPAD(str, 总长度, 填充字符) | 右侧填充字符,使字符串达到指定总长度 | RPAD('123', 5, '*') → 123** | |
| 替换与查找 | REPLACE() | REPLACE(str, 旧子串, 新子串) | 将字符串中所有的旧子串替换为新子串 | REPLACE('hello world', 'world', 'MySQL') → hello MySQL |
| INSTR() | INSTR(str, 子串) | 返回子串第一次出现的位置,找不到返回 0;位置从 1 开始 | INSTR('abcdef', 'cd') → 3 | |
| LOCATE() | LOCATE(子串, str)LOCATE(子串, str, 起始位置) | 查找子串位置,可指定从第几个字符开始查找 | LOCATE('a', 'banana', 2) → 3 | |
| 其他常用 | REVERSE() | REVERSE(str) | 反转字符串顺序 | REVERSE('abc') → cba |
| ELT() | ELT(n, str1, str2, str3…) | 按索引 n 返回对应字符串;n=1 返回 str1,超出范围返回 NULL | ELT(2, '男', '女', '未知') → 女 | |
| FIELD() | FIELD(str, str1, str2, str3…) | 返回字符串在列表中的索引位置;找不到返回 0,与 ELT 功能相反 | FIELD('女', '男', '女', '未知') → 2 |
注意事项
2)数字函数
数值函数用于对数字类型数据执行数学计算、取整、格式化等操作,属于典型的单行函数:逐行运算,每行返回一个计算结果
| 取整与截断 | ROUND() | ROUND(x, 小数位数) | 对数值进行四舍五入;小数位数可省略,默认保留 0 位 | ROUND(3.1415, 2) → 3.14ROUND(3.6) → 4 |
| CEIL() / CEILING() | CEIL(x) | 向上取整,返回大于等于参数的最小整数 | CEIL(3.1) → 4CEIL(-3.1) → -3 | |
| FLOOR() | FLOOR(x) | 向下取整,返回小于等于参数的最大整数 | FLOOR(3.9) → 3FLOOR(-3.1) → -4 | |
| TRUNCATE() | TRUNCATE(x, 小数位数) | 直接截断小数位,不做四舍五入 | TRUNCATE(3.999, 2) → 3.99 | |
| 基础数学运算 | ABS() | ABS(x) | 返回数值的绝对值 | ABS(-10) → 10 |
| MOD() | MOD(x, y) | 取余运算,返回 x 除以 y 的余数,等价于运算符 % | MOD(10, 3) → 1 | |
| POW() / POWER() | POW(x, y) | 幂运算,返回 x 的 y 次方 | POW(2, 3) → 8 | |
| SQRT() | SQRT(x) | 返回非负数的平方根 | SQRT(9) → 3 | |
| 符号与随机数 | SIGN() | SIGN(x) | 返回数值的符号:正数返回 1,负数返回 -1,0 返回 0 | SIGN(-5) → -1 |
| RAND() | RAND()RAND(种子值) | 生成 0~1 之间的随机浮点数;带固定种子时生成可重复的随机序列 | RAND() → 0.628…(随机值)FLOOR(RAND() * 100) → 生成 0~99 的随机整数 | |
| 极值与格式化 | GREATEST() | GREATEST(v1, v2, v3…) | 返回多个参数中的最大值 | GREATEST(5, 12, 8) → 12 |
| LEAST() | LEAST(v1, v2, v3…) | 返回多个参数中的最小值 | LEAST(5, 12, 8) → 5 | |
| FORMAT() | FORMAT(x, 小数位数) | 将数字格式化为千分位格式,返回字符串类型,仅用于展示 | FORMAT(12345.6789, 2) → 12,345.68 |
注意事项
负数取整方向易混淆 CEIL 是往数轴正方向取整,FLOOR 往负方向取整。例如 CEIL(-3.1) = -3,FLOOR(-3.1) = -4,不要凭直觉按 “数字变大变小” 判断。
ROUND 与 TRUNCATE 的本质区别
- ROUND 按四舍五入规则保留小数
- TRUNCATE 是直接砍掉多余小数位,不做进位 例如 ROUND(3.59, 1) = 3.6,而 TRUNCATE(3.59, 1) = 3.5。
随机数通用公式 生成 [min, max] 区间随机整数的通用写法:
FLOOR(最小值 + RAND() * (最大值 – 最小值 + 1))
例:生成 1~100 的随机整数 → FLOOR(1 + RAND() * 100)。
NULL 值陷阱 所有数值函数输入参数为 NULL 时,返回结果也为 NULL,例如 ABS(NULL) 返回 NULL。
FORMAT 返回值是字符串 格式化后的结果带千分位逗号,属于字符串类型,不能直接继续做数学运算,仅用于最终结果展示
3)日期函数
日期函数属于单行函数,逐行对日期 / 时间类型数据执行计算,支持时间获取、分量提取、日期运算、格式转换等操作,是业务开发中的高频函数
年月日用“-”或者“/”分隔
| 当前时间获取 | NOW() / CURRENT_TIMESTAMP() | NOW() | 返回当前系统的日期 + 时间;取语句执行开始时的时间,同一条 SQL 中多次调用值不变 | NOW() → 2026-08-10 15:42:30 |
| SYSDATE() | SYSDATE() | 返回函数执行瞬间的实时时间;同一条 SQL 中多次调用值可能不同 | SYSDATE() → 2026-08-10 15:42:30 | |
| CURDATE() / CURRENT_DATE() | CURDATE() | 仅返回当前日期,不包含时间 | CURDATE() → 2026-08-10 | |
| CURTIME() / CURRENT_TIME() | CURTIME() | 仅返回当前时间,不包含日期 | CURTIME() → 15:42:30 | |
| 日期分量提取 | YEAR() / MONTH() / DAY() | YEAR(date) | 分别提取日期中的年、月、日数值 | YEAR('2026-08-10') → 2026MONTH('2026-08-10') → 8 |
| HOUR() / MINUTE() / SECOND() | HOUR(time) | 分别提取时间中的时、分、秒数值 | HOUR('15:42:30') → 15 | |
| DAYOFYEAR() | DAYOFYEAR(date) | 返回日期是当年的第几天(1~366) | DAYOFYEAR('2026-02-01') → 32 | |
| DAYOFWEEK() | DAYOFWEEK(date) | 返回日期是当周的第几天(周日 = 1,周六 = 7) | DAYOFWEEK('2026-08-10') → 2(周一) | |
| DATE() / TIME() | DATE(datetime) | 从 datetime 类型中单独提取日期 / 时间部分 | DATE(NOW()) → 2026-08-10 | |
| 日期加减运算 | DATE_ADD() / ADDDATE() | DATE_ADD(date, INTERVAL 数值 单位) | 对日期执行加法运算,支持年 / 月 / 日 / 时 / 分 / 秒等单位 | DATE_ADD('2026-08-10', INTERVAL 3 DAY) → 2026-08-13DATE_ADD(NOW(), INTERVAL 1 MONTH) → 一个月后 |
| DATE_SUB() / SUBDATE() | DATE_SUB(date, INTERVAL 数值 单位) | 对日期执行减法运算,单位与 DATE_ADD 一致 | DATE_SUB('2026-08-10', INTERVAL 2 YEAR) → 2024-08-10 | |
| LAST_DAY() | LAST_DAY(date) | 返回日期所在月份的最后一天 | LAST_DAY('2026-02-01') → 2026-02-28 | |
| 日期差值计算 | DATEDIFF() | DATEDIFF(date1, date2) | 计算两个日期相差的天数,规则:date1 – date2,结果可正可负 | DATEDIFF('2026-08-10', '2026-08-01') → 9 |
| TIMEDIFF() | TIMEDIFF(time1, time2) | 计算两个时间的差值,返回 time 类型 | TIMEDIFF('18:00:00', '09:00:00') → 09:00:00 | |
| TIMESTAMPDIFF() | TIMESTAMPDIFF(单位, date1, date2) | 按指定单位计算时间差,规则:date2 – date1;常用单位:YEAR/MONTH/DAY/HOUR/MINUTE | TIMESTAMPDIFF(YEAR, '2000-01-01', '2026-08-10') → 26 | |
| 格式化与转换 | DATE_FORMAT() | DATE_FORMAT(date, 格式串) | 将日期 / 时间转为指定格式的字符串,最常用格式:%Y-%m-%d %H:%i:%s | DATE_FORMAT(NOW(), '%Y年%m月%d日') → 2026年08月10日 |
| STR_TO_DATE() | STR_TO_DATE(字符串, 格式串) | 将符合格式的字符串转换为日期类型,是 DATE_FORMAT 的反向操作 | STR_TO_DATE('08-10-2026', '%m-%d-%Y') → 2026-08-10 | |
| 其他常用 | WEEK() / YEARWEEK() | WEEK(date) | 返回日期是当年的第几周;YEARWEEK 返回 年 + 周 组合值 | WEEK('2026-01-05') → 1 |
| MONTHNAME() / DAYNAME() | MONTHNAME(date) | 返回月份 / 星期的英文名称 | MONTHNAME('2026-08-10') → August |
注意事项
格式符大小写敏感 DATE_FORMAT 的格式符区分大小写,最容易写错的几个:
- %Y = 4 位年份(2026),%y = 2 位年份(26)
- %H = 24 小时制,%h = 12 小时制
- %i = 分钟,%s = 秒;分钟不是 %m(%m 是月份)
差值函数参数顺序相反
- DATEDIFF(date1, date2):前减后(date1 – date2)
- TIMESTAMPDIFF(单位, date1, date2):后减前(date2 – date1) 两者顺序相反,是新手最容易写反、导致结果正负错误的点。
NOW() 与 SYSDATE() 的区别 NOW() 取 SQL 语句开始执行的时间,同一条 SQL 里多次调用值完全一致;SYSDATE() 取函数执行瞬间的实时时间,长 SQL 中多次调用会有差异。绝大多数业务场景使用 NOW() 即可。
索引失效风险 不要在 WHERE 子句中对日期索引字段使用函数,例如 WHERE YEAR(create_time) = 2026 会导致索引失效。推荐改写为范围查询:
WHERE create_time >= '2026-01-01' AND create_time < '2027-01-01'
NULL 值陷阱 所有日期函数的输入参数为 NULL 时,返回结果也为 NULL
4)转换函数(显式)
转换函数用于显式转换数据的类型、字符集或格式,属于单行函数。核心作用是规避隐式转换带来的逻辑异常、索引失效等问题,统一数据类型后再进行运算、比较或关联
| 核心类型转换 | CAST() | CAST(表达式 AS 目标类型) | 标准 SQL 语法的类型转换函数,兼容性最强,将表达式的值转换为指定数据类型 | CAST('123' AS SIGNED) → 123CAST(3.14 AS DECIMAL(3,1)) → 3.1CAST('2026-08-10' AS DATE) → 2026-08-10 |
| CONVERT()(类型转换) | CONVERT(表达式, 目标类型) | MySQL 专属类型转换,功能与 CAST 完全一致,仅语法格式不同(用逗号分隔) | CONVERT('456abc', UNSIGNED) → 456 | |
| 字符集与校对转换 | CONVERT()(字符集转换) | CONVERT(表达式 USING 目标字符集) | 将字符串转换为指定字符集,解决跨字符集乱码、关联比对异常问题 | CONVERT('测试' USING gbk) → gbk 编码的字符串 |
| BINARY | BINARY 表达式 | 强制将字符串转为二进制字符串,按字节逐位比对,天然大小写敏感 | BINARY 'Admin' = 'admin' → 0(不相等) | |
| CHARSET() | CHARSET(字符串) | 返回当前字符串所属的字符集 | CHARSET('你好') → utf8mb4 | |
| COLLATION() | COLLATION(字符串) | 返回当前字符串的校对规则 | COLLATION('abc') → utf8mb4_0900_ai_ci | |
| 常用补充转换 | STR_TO_DATE() | STR_TO_DATE(字符串, 格式串) | 将格式化字符串转换为 DATE/DATETIME 类型(日期类核心转换函数) | STR_TO_DATE('2026-08-10', '%Y-%m-%d') → 2026-08-10 |
| DATE_FORMAT() | DATE_FORMAT(日期, 格式串) | 将日期类型转换为指定格式的字符串(日期类核心转换函数) | DATE_FORMAT(NOW(), '%Y%m%d') → 20260810 |
注意事项
常用目标类型清单 CAST / CONVERT 支持的常用转换目标类型:
- 字符串:CHAR(n)
- 整数:SIGNED(有符号)、UNSIGNED(无符号)
- 小数:DECIMAL(M,D),需指定总长度和小数位数
- 日期时间:DATE、DATETIME、TIME
隐式转换的两大坑 MySQL 会自动进行隐式类型转换(例如字符串和数字比较时,字符串会自动转成数字),但极易出问题:
- 索引失效:对索引字段发生隐式转换时,索引会直接失效,查询性能骤降
- 逻辑错误:非纯数字字符串转数字时,会从左截取到非数字字符为止,例如 '123abc' 会被转成 123,纯字母 'abc' 会被转成 0,极易引发数据错误。
开发规范:尽量使用显式转换函数,保证逻辑可控。
转换失败不报错 MySQL 的类型转换失败不会抛出异常,而是静默返回 0 或 NULL。例如 CAST('hello' AS SIGNED) 返回 0,CAST('非法日期' AS DATE) 返回 NULL,调试时需要特别注意。
CAST 与 CONVERT 选型建议
- 日常类型转换优先用 CAST:属于标准 SQL 语法,兼容 Oracle、PostgreSQL 等所有数据库,迁移成本低
- 仅在需要切换字符集时,使用 CONVERT(… USING …) 的专属语法
BINARY 性能问题 BINARY 虽然可以临时实现大小写敏感比对,但会导致索引失效,仅适合临时查询使用;长期业务需求建议直接修改字段的校对规则为 utf8mb4_bin
5)流程控制函数
流程控制函数用于在 SQL 中实现条件分支、空值兜底等逻辑判断,属于单行函数,逐行运算并返回结果,可替代简单的分支逻辑,无需在应用层二次处理
| 空值兜底类 | IFNULL() | IFNULL(expr1, expr2) | 空值替换:若第一个表达式为 NULL,返回第二个值;否则返回第一个表达式本身 | IFNULL(salary, 0) → 薪资为空时返回 0IFNULL(NULL, '默认值') → 默认值 |
| COALESCE() | COALESCE(expr1, expr2, …) | 返回参数列表中第一个非 NULL 的值;所有参数都为 NULL 则返回 NULL;支持任意多个参数,是通用的空值兜底函数 | COALESCE(NULL, NULL, 0, 100) → 0 | |
| NULLIF() | NULLIF(expr1, expr2) | 反向空值转换:若两个表达式的值相等,返回 NULL;不相等则返回第一个表达式的值 | NULLIF(score, 0) → 分数为 0 时返回 NULLNULLIF(5, 3) → 5 | |
| 分支判断类 | IF() | IF(判断条件, 成立返回值, 不成立返回值) | 单分支条件判断,等价于编程语言中的三元运算符 | IF(age >= 18, '成年', '未成年')IF(salary > 5000, '高薪', '普通') |
| CASE 表达式 WHEN …(简单 CASE) | CASE 目标表达式<br> WHEN 值1 THEN 结果1<br> WHEN 值2 THEN 结果2<br> …<br> ELSE 默认值<br>END | 对单个表达式做等值匹配,匹配到对应值则返回对应结果;都不匹配返回 ELSE 的默认值;省略 ELSE 则返回 NULL | CASE gender<br> WHEN 1 THEN '男'<br> WHEN 2 THEN '女'<br> ELSE '未知'<br>END | |
| CASE WHEN …(搜索 CASE) | CASE<br> WHEN 条件1 THEN 结果1<br> WHEN 条件2 THEN 结果2<br> …<br> ELSE 默认值<br>END | 支持多条件、复杂范围判断,每个 WHEN 后可写独立的判断条件,是最灵活的分支写法 | CASE<br> WHEN score >= 90 THEN '优秀'<br> WHEN score >= 60 THEN '及格'<br> ELSE '不及格'<br>END |
注意事项
CASE 必须以 END 结尾 忘记写 END 是最常见的语法错误;CASE 是一个表达式,最终会返回一个值,可以和普通字段一样设置别名。
两种 CASE 的适用场景
- 仅做等值匹配(如状态编码转中文含义),用简单 CASE,写法更简洁
- 涉及范围判断、多字段组合判断,必须用搜索 CASE
返回值类型尽量统一 同一个 CASE 中,所有 THEN 返回的数据类型建议保持一致,避免隐式类型转换导致的异常结果。
IFNULL 与 COALESCE 选型
- 仅两个参数的简单空值兜底,用 IFNULL 写法更简短
- 多个备选值、需要兼容标准 SQL,优先用 COALESCE
避免过度嵌套 流程控制函数支持嵌套(如 IF 里套 IF),但嵌套过深会严重降低 SQL 可读性,复杂逻辑建议在应用层处理。
注意与流程控制语句区分 这里的 IF()、CASE 都是单行函数 / 表达式,用在 SELECT、WHERE 等子句中;和存储过程 / 自定义函数里的 IF 语句、WHILE 循环不是同一个概念
6)使用原则
简单处理放数据库,复杂逻辑放应用层 函数适合做单行的格式转换、简单计算;复杂的多层嵌套判断、业务规则,建议放到 Java/Python 等应用代码里处理,SQL 可读性和维护性更好。
WHERE 条件中,不要对索引字段使用函数 比如 WHERE YEAR(create_time) = 2026 会导致 create_time 上的索引失效,一定要改写成范围查询:
WHERE create_time >= '2026-01-01' AND create_time < '2027-01-01'
警惕 NULL 传染 绝大多数函数,只要输入参数有 NULL,返回结果就是 NULL。比如 salary + NULL = NULL、CONCAT('姓名', NULL) = NULL。涉及可能为空的字段,一定要先用 IFNULL() 做兜底。
不要过度嵌套函数 多层嵌套的函数会让 SQL 很难读懂和维护,能简化就简化,能在应用层处理就不要堆在 SQL 里
(2)多行函数/聚合函数
多行函数也叫组函数、聚合函数,作用是对一组数据行进行汇总运算,每组最终只返回一个计算结果,常与 GROUP BY 分组子句搭配,实现维度统计、汇总计算等业务需求
与单行函数核心差异:单行函数逐行计算、一行对应一个结果;多行函数按组聚合、一组对应一个结果
| 计数统计 | COUNT(*) | COUNT(*) | 统计结果集的总行数,不会忽略 NULL 值行 | COUNT(*) → 统计员工总人数 |
| COUNT(字段名) | COUNT(column) | 统计指定字段的非空行数,自动忽略值为 NULL 的行 | COUNT(bonus) → 统计有奖金的员工人数 | |
| COUNT(DISTINCT 字段名) | COUNT(DISTINCT column) | 统计指定字段去重后的非空行数 | COUNT(DISTINCT dept_id) → 统计有员工的部门数量 | |
| 数值计算 | SUM() | SUM(column) | 对指定数值字段求和,自动忽略 NULL 值 | SUM(salary) → 计算全公司薪资总和 |
| AVG() | AVG(column) | 计算指定数值字段的平均值,自动忽略 NULL 值 | AVG(salary) → 计算员工平均薪资 | |
| 极值统计 | MAX() | MAX(column) | 返回指定字段的最大值,支持数值、日期、字符串类型,自动忽略 NULL | MAX(salary) → 查询全公司最高薪资 |
| MIN() | MIN(column) | 返回指定字段的最小值,支持数值、日期、字符串类型,自动忽略 NULL | MIN(create_time) → 查询最早的入职时间 |
核心搭配语法
1. GROUP BY 分组
当需要按维度拆分统计(如按部门、按岗位、按日期)时,使用 GROUP BY 将数据划分为多个组,聚合函数会针对每个组分别计算。
— 统计每个部门的员工人数、平均薪资、最高薪资
SELECT
dept_id,
COUNT(*) AS 员工人数,
AVG(salary) AS 平均薪资,
MAX(salary) AS 最高薪资
FROM emp
GROUP BY dept_id;
语法规则:SELECT 子句中,除聚合函数外的普通字段,必须同时出现在 GROUP BY 子句中,否则会出现逻辑错误或非预期结果。
2. HAVING 分组后筛选
用于对分组聚合后的结果做条件过滤,必须与 GROUP BY 配合使用。
— 筛选出平均薪资大于 8000 的部门
SELECT
dept_id,
AVG(salary) AS 平均薪资
FROM emp
GROUP BY dept_id
HAVING AVG(salary) > 8000;
WHERE 与 HAVING 的核心区别:
- WHERE:分组聚合之前筛选原始数据行,不能使用聚合函数作为条件
- HAVING:分组聚合之后筛选分组结果,条件只能是分组字段或聚合函数
- 执行顺序:FROM → JOIN → WHERE → GROUP BY → 聚合计算 → HAVING → ORDER BY → LIMIT
注意事项
NULL 值自动忽略 所有聚合函数都会自动跳过值为 NULL 的行,不参与计算。例如 AVG(bonus) 仅计算有奖金的员工,NULL 行不会计入分母;若需将 NULL 按 0 计算,需先用 IFNULL(bonus, 0) 做兜底处理。
COUNT (*) 与 COUNT (字段) 的差异
- COUNT(*):统计总行数,包含 NULL 行,性能最优,是通用计数写法
- COUNT(字段):仅统计该字段非空的行数
- 无特殊空值过滤需求时,优先使用 COUNT(*)。
WHERE 中禁止使用聚合函数 聚合函数的计算晚于 WHERE 执行,因此 WHERE 子句中不能写聚合函数条件;分组后的筛选必须使用 HAVING。
支持多字段分组 GROUP BY 可指定多个字段实现组合维度统计,例如 GROUP BY dept_id, job 表示按「部门 + 岗位」的组合维度分组。
去重聚合 聚合函数可搭配 DISTINCT 实现去重统计,例如 COUNT(DISTINCT user_id) 统计去重后的用户数,避免重复数据干扰统计结果。
全局聚合规则 不写 GROUP BY 时,整张表会被默认为一个组,最终只返回一行聚合结果,适用于全表整体统计的场景。
4.用户管理
MySQL是一个多用户的数据库系统,按照权限,用户可以分为两种:root(超级管理员)用户和由root用户创建的普通用户
(1)创建用户
CREATE USER username IDENTIFIED BY 'password';
示例:创建一个允许任意主机登录的普通用户 test_user
CREATE USER 'test_user'@'%' IDENTIFIED BY '123456';
说明:用户名后需搭配主机地址,格式为用户名@主机地址;省略主机地址时,默认值为%。
(2)查看用户
SELECT USER,HOST FROM mysql.user;
在MySQL数据库安装完后会有一个默认的表叫做mysql,这里面有一个表叫user记录了所有的用户,我们查询的是这个表中的两个列,USER是用户名,而HOST则是用户权限
- localhost:仅允许从数据库本机登录
- 127.0.0.1:通过TCP/IP协议在本机链接
- ::1:兼容支持ipv6,表示同ipv4的本机访问
- %:允许从任意远程主机登录
- 指定 IP 地址:仅允许从对应 IP 的主机登录
(3)修改用户
1)修改用户密码
ALTER USER 用户名@主机地址 IDENTIFIED BY '新密码';
示例:修改 test_user 的登录密码
ALTER USER 'test_user'@'%' IDENTIFIED BY 'new_password_666';
说明:ALTER USER是 MySQL 官方推荐的标准改密语法,兼容性与安全性最优。
2)修改用户名与登录主机
RENAME USER 旧用户名@旧主机地址 TO 新用户名@新主机地址;
示例:将用户的登录范围从任意主机限制为仅本地登录
RENAME USER 'test_user'@'%' TO 'test_user'@'localhost';
(4)删除用户
DROP USER 用户名@主机地址;
示例:删除 test_user 用户
DROP USER 'test_user'@'%';
说明:MySQL 以「用户名 + 主机地址」作为用户唯一标识,删除时必须同时指定两者才能精准匹配;用户删除后,其名下所有权限会被同步清除。
(5)权限管理(DCL)
新创建的用户默认没有任何操作权限,仅能登录数据库,无法读写数据;需要由管理员账号授予对应权限后,才能执行对应的数据库操作。权限按粒度从大到小可分为全局权限、库级权限、表级权限、列级权限。
1)常用权限说明
上面有表,这是一部分
- ALL PRIVILEGES:除授权外的所有操作权限
- SELECT:查询数据权限
- INSERT:插入数据权限
- UPDATE:修改数据权限
- DELETE:删除数据权限
- CREATE:创建数据库、数据表权限
- DROP:删除数据库、数据表权限
2)授予权限
GRANT 权限列表 ON 数据库名.表名 TO 用户名@主机地址;
示例:
— 给用户授予 test 库下所有表的查询、插入、修改权限
GRANT SELECT,INSERT,UPDATE ON test.* TO 'test_user'@'%';
— 给用户授予所有库所有表的全部权限(超级管理员权限,生产环境谨慎使用)
GRANT ALL PRIVILEGES ON *.* TO 'admin_user'@'%';
3)查看用户权限
SHOW GRANTS FOR 用户名@主机地址;
示例:
SHOW GRANTS FOR 'test_user'@'%';
4)撤销权限
REVOKE 权限列表 ON 数据库名.表名 FROM 用户名@主机地址;
示例:
— 撤销用户对 test 库的删除数据权限
REVOKE DELETE ON test.* FROM 'test_user'@'%';
(6)刷新权限
FLUSH PRIVILEGES;
说明:如果通过直接修改mysql.user等系统表的方式操作用户与权限,需要执行该命令让配置立即生效;通过CREATE USER、GRANT、REVOKE等标准 SQL 操作时,MySQL 会自动刷新权限,无需手动执行。
(7)使用注意事项
1)用户唯一标识:
MySQL 中「用户名 + 主机地址」共同组成一个完整用户,同名不同主机视为两个独立账号,权限互不影响。
2)最小权限原则:
仅授予业务必需的最小权限,禁止随意开放全局ALL PRIVILEGES权限,降低数据安全风险。
3)访问范围管控:
生产环境尽量避免使用%开放全主机登录,应限制为指定业务 IP 访问。
4)root 账号规范:
root 超级管理员账号仅用于本地运维配置,禁止直接用于业务系统连接数据库。
(后续可能会更新)

