前言
MySQL是我们常用的关系型数据库,今天我们重新回顾一下这个老朋友。
基础概念
数据库(Database):数据库是按照数据结构来组织、存储和管理数据的仓库。
表(Table):表是数据库中用于存储数据的结构,由行(记录)和列(字段)组成。
字段(Field):字段是表中数据的属性,定义了数据的类型和特征。
记录(Record):记录是表中的一行数据,包含所有字段的信息。
主键(Primary Key):主键是表中的一个字段或字段组合,其值在表中唯一,用于唯一标识一条记录。
索引(Index):索引是对表中数据进行排序和检索的一种数据结构,可以加快查询速度。

SQL类型
- DDL(Data Definition Language,数据定义语言)用于定义和修改数据库结构,如创建、删除、修改表等;
- DML(Data Manipulation Language,数据操纵语言)用于对数据库中的数据进行操作,如插入、更新、删除数据等;
- DCL(Data Control Language,数据控制语言)用于控制用户对数据库的访问权限,如授权、撤销权限等。
数据类型
数值类型
- 整数类型 - TINYINT:占用1个字节,有符号范围为-128到127,无符号范围为0到255。
- SMALLINT:占用2个字节,有符号范围为-32768到32767,无符号范围为0到65535。
- MEDIUMINT:占用3个字节,有符号范围为-8388608到8388607,无符号范围为0到16777215。
- INT或- INTEGER:占用4个字节,有符号范围为-2147483648到2147483647,无符号范围为0到4294967295。
- BIGINT:占用8个字节,有符号范围为-9223372036854775808到9223372036854775807,无符号范围为0到18446744073709551615。
 
- 浮点数类型: - FLOAT:单精度浮点数,占用4个字节。
- DOUBLE或- DOUBLE PRECISION:双精度浮点数,占用8个字节。
- DECIMAL或- NUMERIC:用于存储精确的小数,需要指定精度和小数位数。
 
- 位类型: - BIT(M):用于存储位字段值,- M表示位值的长度。
 
日期和时间类型
- DATE:格式为- YYYY-MM-DD,仅存储日期信息。
- TIME:格式为- HH:MM:SS,仅存储时间信息,可以包含 fractional seconds precision(小数秒精度)。
- DATETIME:格式为- YYYY-MM-DD HH:MM:SS,同时存储日期和时间信息,可以包含 fractional seconds precision。
- TIMESTAMP:类似于- DATETIME,但范围有限,适合存储时间戳,可以包含 fractional seconds precision。
- YEAR:用于存储年份信息,格式为- YYYY。
字符串类型
- 字符类型: - CHAR(M):固定长度字符串,- M表示最大长度,存储效率高,适用于存储固定长度的数据,如身份证号、MD5值等。
- VARCHAR(M):可变长度字符串,- M表示最大长度,节省存储空间,适用于存储长度不固定的数据,如用户名、地址等。
 
- 文本类型: - TINYTEXT:用于存储较小的文本数据。
- TEXT:用于存储较大的文本数据,如博客内容、评论等。
- MEDIUMTEXT:用于存储非常大的文本数据。
- LONGTEXT:用于存储极大的文本数据。
 
- 枚举类型: - ENUM:用于存储有限值的集合,如性别、状态等固定选项。
 
- 集合类型: - SET:支持存储多个选项的组合,如用户权限、兴趣标签等。
 
二进制类型
- 二进制字符串类型: - BINARY:固定长度的二进制字符串。
- VARBINARY:可变长度的二进制字符串。
 
- 二进制大对象类型: - TINYBLOB:用于存储较小的二进制数据。
- BLOB:用于存储较大的二进制数据,如图片、音频、视频等。
- MEDIUMBLOB:用于存储非常大的二进制数据。
- LONGBLOB:用于存储极大的二进制数据。
 
空间数据类型
- MySQL支持空间数据类型,用于存储和操作地理空间数据,如点、线、多边形等。
JSON数据类型
- JSON:用于存储JSON格式的数据,方便对JSON数据进行查询和操作。
选择数据类型的依据
- 存储效率:合理分配字段的存储大小,避免浪费。例如,根据数据的实际范围选择合适的整数类型。
- 数据准确性:确保字段只能存储符合业务逻辑的数据。例如,年龄字段应选择整数类型,且范围应符合实际年龄的可能值。
- 查询性能:优化查询速度,减少不必要的计算和转换。例如,对于经常需要进行范围查询的数值字段,选择合适的数据类型和索引可以提高查询效率。
- 业务需求:根据具体的应用场景和业务需求选择合适的数据类型。例如,金融计算需要高精度,应选择DECIMAL类型;而科学计算对精度要求不高,可以选择FLOAT或DOUBLE类型。对于日期和时间数据,根据需要存储的信息精度选择DATE、TIME、DATETIME或TIMESTAMP类型。
DDL语句
MySQL的DDL语句(Data Definition Language,数据定义语言)用于定义和修改数据库结构,包括创建、修改、删除数据库和表等对象。
数据库操作
- 查询所有数据库
| 1 | show databases; | 
- 查询当前数据库
| 1 | select database(); | 
- 创建数据库
| 1 | create database [if not exists] 数据库名 [default charset 字符集] [collate 排序规则]; | 
- 删除数据库
| 1 | drop database [if exists] 数据库名; | 
- 使用数据库
| 1 | USE 数据库名; | 
表操作
- 查询当前数据库所有的表
| 1 | show tables; | 
- 查看指定表结构
| 1 | desc 表名 | 
- 查询指定表的建表语句
| 1 | show create table 表名; | 
- 删除表
| 1 | DROP TABLE [IF EXISTS] 表名; | 
- 删除数据,但保留表结构
| 1 | TRUNCATE TABLE 表名; | 
- 创建表结构
| 1 | CREATE TABLE 表名( | 
- 表修改 - 添加字段
 - 1 
 2
 3- ALTER TABLE 表名 ADD 字段名 数据类型 [默认值 非空约束] [COMMENT 注释]; 
 # 示例
 ALTER TABLE users ADD email VARCHAR(100);- 修改某字段的数据类型
 - 1 
 2
 3- ALTER TABLE 表名 MODIFY 字段名 新数据类型 [默认值 非空约束] [COMMENT 注释]; 
 # 示例
 ALTER TABLE users MODIFY username VARCHAR(100) NOT NULL COMMENT '用户名';- 修改字段名和字段类型
 - 1 
 2
 3- ALTER TABLE 表名 CHANGE 旧字段名 新字段名 新数据类型 [默认值 非空约束] [COMMENT 注释] ; 
 # 示例
 ALTER TABLE users CHANGE nickname nick VARCHAR(30);- 删除某字段
 - 1 
 2
 3- ALTER TABLE 表名 DROP 字段名; 
 # 示例
 ALTER TABLE users DROP COLUMN email;- 修改表名
 - 1 
 2
 3- ALTER TABLE 表名 RENAME TO 新表名; 
 # 示例
 ALTER TABLE tb_user RENAME TO employee;
索引操作
- 创建索引
| 1 | -- UNIQUE 表示添加唯一索引,Index 表示是普通索引 | 
- 删除索引
| 1 | DROP INDEX 索引名 ON 表名; | 
- 查看索引
| 1 | SHOW INDEX FROM 表名称; | 
视图操作
视图是基于 SQL 查询结果集的虚拟表,它不实际存储数据,而是存储查询定义(也就是三级模式中的外模式)。
视图的优点如下:
| 1 | - 简化复杂查询:将复杂的 SQL 查询封装为简单的表形式(需要注意性能问题) | 
- 创建视图
| 1 | CREATE [OR REPLACE] [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}] | 
- 删除视图
| 1 | drop view 视图名称; | 
DML语句
MySQL的DML语句(Data Manipulation Language, 数据操纵语言)主要包括插入(INSERT)、更新(UPDATE)、删除(DELETE)和查询(SELECT)语句,以下是按分类列举的示例:
插入 INSERT
- 插入单条完整记录
| 1 | INSERT INTO 表名 (列1, 列2, ..., 列n) VALUES (值1, 值2, ..., 值n); | 
- 插入多条记录
| 1 | INSERT INTO 表名 (列1, 列2, ..., 列n) | 
- 插入单条部分记录(省略列名,值顺序与表中列顺序一致)
| 1 | INSERT INTO 表名 VALUES (值1, 值2, ..., 值n); | 
- 插入结果查询
| 1 | INSERT INTO 表名 (列1, 列2, ..., 列n) SELECT 列1, 列2, ..., 列n FROM 另一个表名 WHERE 条件; | 
更新 UPDATE
- 更新单个字段
| 1 | UPDATE 表名 SET 列名 = 新值 WHERE 条件; | 
- 更新多个字段
| 1 | UPDATE 表名 SET 列1 = 新值1, 列2 = 新值2, ..., 列n = 新值n WHERE 条件; | 
- 无条件更新(全表更新)
| 1 | UPDATE 表名 SET 列名 = 新值; | 
删除 DELETE
- 条件删除
| 1 | DELETE FROM 表名 WHERE 条件; | 
- 删除所有数据
| 1 | DELETE FROM 表名; | 
查询 SELECT
查询是数据库操作的重点,为了更好的说明,我们先来新建两张表并插入几条测试数据
示例数据表
- users(用户表)
| id | name | age | gender | city | phone | register_time | |
|---|---|---|---|---|---|---|---|
| 1 | 张三 | 22 | 男 | 北京 | zhangsan@test.com | 13800138 | 2023-01-15 08:30:00 | 
| 2 | 李四 | 28 | 男 | 上海 | lisi@test.com | NULL | 2023-02-20 10:15:00 | 
| 3 | 王五 | 25 | 女 | 广州 | NULL | 13900139 | 2023-03-05 14:20:00 | 
| 4 | 赵六 | 30 | 男 | 北京 | zhaoliu@test.com | 13700137 | 2023-01-10 09:45:00 | 
| 5 | 孙七 | 23 | 女 | 深圳 | sunqi@test.com | 13600136 | 2023-02-18 16:00:00 | 
| 6 | 周八 | 35 | 男 | 上海 | zhouba@test.com | 13500135 | 2022-12-05 11:30:00 | 
| 7 | 吴九 | 19 | 女 | 广州 | wujiu@test.com | NULL | 2023-03-20 15:10:00 | 
- orders(订单表)
| order_id | user_id | product_name | price | order_time | 
|---|---|---|---|---|
| 101 | 1 | 手机 | 3999 | 2023-01-20 10:00:00 | 
| 102 | 1 | 耳机 | 799 | 2023-02-05 14:30:00 | 
| 103 | 4 | 电脑 | 5999 | 2023-01-12 09:15:00 | 
| 104 | 5 | 平板 | 2999 | 2023-02-25 16:40:00 | 
| 105 | 6 | 手表 | 1599 | 2022-12-10 11:20:00 | 
| 106 | NULL | 键盘 | 299 | 2023-03-01 08:50:00 | 
- 创建表结构及插入数据的SQL
| 1 | CREATE TABLE users ( | 
基础查询关键字
SELECT + FROM
- 解释:- SELECT:指定要查询的列(投影操作),- SELECT *表示查询所有列。
- FROM:指定数据来源的表(或视图、子查询等)。
 
- 示例:
| 1 | -- 查询 users 表中所有列 | 
条件筛选关键字
WHERE
- 解释:用于过滤行,只返回满足条件的记录。支持比较运算符( - =、- >、- <等)、逻辑运算符(- AND、- OR)、通配符(- LIKE)等。- 支持逻辑运算符(AND、OR)用于组合多个条件:- AND:需同时满足所有条件才返回记录(逻辑 “与”);
- OR:只要满足任一条件就返回记录(逻辑 “或”);
 
 
- 支持逻辑运算符(
- 示例 - 1 
 2
 3
 4
 5
 6- -- 查询 age 大于 18 且 gender 为 '男' 的用户 
 SELECT name, age FROM users
 WHERE age > 18 AND gender = '男';
 -- 查询 name 以 '张' 开头的用户
 SELECT name FROM users WHERE name LIKE '张%';
BETWEEN ... AND ...
- 解释:范围查询:筛选列值在指定范围内的记录(包含边界值),一般用于数字类型和日期类型字段的查询。 
- 示例: - 1 
 2- -- 查询 age 在 20 到 30 之间的用户 
 SELECT name, age FROM users WHERE age BETWEEN 20 AND 30;
IN
- 解释:判断列值是否在指定的列表中,简化多个 - OR条件。
- 示例: - 1 
 2- -- 查询来自 '北京'、'上海'、'广州' 的用户 
 SELECT name, city FROM users WHERE city IN ('北京', '上海', '广州');
IS NULL / IS NOT NULL
- 解释:判断列值是否为 - NULL(注意:- NULL表示无值,不能用- =比较)。
- 示例: - 1 
 2
 3
 4
 5- -- 查询未填写邮箱的用户 
 SELECT name FROM users WHERE email IS NULL;
 -- 查询已填写手机号的用户
 SELECT name FROM users WHERE phone IS NOT NULL;
排序关键字
ORDER BY
- 解释:对查询结果按指定列排序,默认升序( - ASC),可指定降序(- DESC)。
- 示例: - 1 
 2
 3
 4
 5- -- 按 age 升序排列(默认),若年龄相同则按注册时间降序 
 SELECT name, age, register_time FROM users
 ORDER BY age, register_time DESC;
 -- 结果先按 age 从小到大排序,年龄相同的用户再按 register_time 从新到旧排序。示例中所有用户年龄均不同,直接按年龄升序排列
聚合与分组关键字
聚合函数
- 解释:对一组数据进行计算并返回单一结果 - COUNT(column):统计非- NULL值的行数;
- SUM(column):计算列值总和;
- AVG(column):计算列值平均值;
- MAX(column)/- MIN(column):获取列的最大值/最小值。
 
- 示例: - 1 
 2
 3
 4
 5
 6
 7
 8
 9- -- 统计用户总数 
 -- 注意:COUNT(column):仅统计该列非 NULL 值的行数; COUNT(*) 统计所有行(包括 NULL值的行数)
 SELECT COUNT(*) AS total_users FROM users;
 -- 计算所有商品的平均价格
 SELECT AVG(price) AS avg_price FROM orders;
 -- 获取最大年龄和最小年龄
 SELECT MAX(age) AS max_age, MIN(age) AS min_age FROM users;
GROUP BY
- 解释:将结果按指定列分组,通常与聚合函数搭配使用,对每组数据进行聚合计算。 
- 示例: - 1 
 2- -- 按 gender 分组,统计每组的用户数量 
 SELECT gender, COUNT(*) AS user_count FROM users GROUP BY gender;
HAVING
- 解释:对 - GROUP BY分组后的结果进行筛选(- WHERE用于分组前筛选行,- HAVING用于分组后筛选组)。
- 示例: - 1 
 2
 3
 4
 5- -- 按 city 分组,筛选出用户数 > 1 的城市 
 SELECT city, COUNT(*) AS user_count
 FROM users
 GROUP BY city
 HAVING user_count > 1;
限制与偏移关键字
LIMIT
- 解释:限制返回的行数,常用于分页查询。语法: - LIMIT 起始位置, 行数(起始位置默认从0开始)。
- 示例: - 1 
 2
 3
 4
 5- -- 返回前 3 条用户记录 
 SELECT name, age FROM users LIMIT 3;
 -- 分页查询:从第 3 条开始(起始位置2),返回 2 条(第4-5条)
 SELECT name, age FROM users LIMIT 2, 2;
连接查询关键字
ON(连接条件)
- 解释:在连接查询( - JOIN,默认就是- inner join)中用于指定表之间的关联条件,定义两表如何建立连接关系。与- WHERE不同,- ON是专门用于连接时的条件筛选,仅作用于关联的表。
- 示例:通过 - users.id = orders.user_id关联两表,查询用户姓名和对应的订单商品:- 1 
 2
 3
 4- -- 查询用户及其订单信息(只显示用户有订单,且订单的用户也存在的 数据) 
 SELECT u.name, o.product_name
 FROM users u
 JOIN orders o ON u.id = o.user_id;
INNER JOIN(内连接)
- 解释:只返回两表中匹配条件的行(交集)。 
- 示例: - 1 
 2
 3
 4- -- 查询用户及其订单信息(只显示用户有订单,且订单的用户也存在的 数据) 
 SELECT u.name, o.product_name
 FROM users u
 INNER JOIN orders o ON u.id = o.user_id;
LEFT JOIN(左连接)
- 解释:返回左表所有行,右表中无匹配的行用 - NULL填充。
- 示例: - 1 
 2
 3
 4
 5
 6- -- 查询所有用户及其订单(包括无订单的用户) 
 SELECT u.name, o.order_id
 FROM users u
 LEFT JOIN orders o ON u.id = o.user_id;
 -- 返回所有7个用户,有订单的显示订单ID,无订单的 order_id 为 NULL(李四、王五、吴九无订单)
RIGHT JOIN(右连接)
- 解释:返回右表所有行,左表中无匹配的行用 - NULL填充(与左连接相反)。
- 示例: - 1 
 2
 3
 4- -- 查询所有订单及其对应的用户(包括无用户信息的订单) 
 SELECT o.order_id, u.name
 FROM users u
 RIGHT JOIN orders o ON u.id = o.user_id;
UNION(合并+去重)
- 解释:用于合并两个或多个 - SELECT语句的结果集,要求各查询返回的列数、列顺序和数据类型必须一致。合并结果集并去除重复记录(相当于对合并后的数据进行去重);
- 示例: - 1 
 2
 3
 4- -- 合并北京和上海的用户姓名(去重,实际两城市无重名) 
 SELECT name FROM users WHERE city = '北京'
 UNION
 SELECT name FROM users WHERE city = '上海';
UNION ALL(合并)
- 解释:仅合并结果集,保留所有记录(包括重复项),执行效率高于 - UNION(因无需去重)。
- 示例: - 1 
 2
 3
 4- -- 合并北京和上海的用户姓名(保留重复,实际无重复) 
 SELECT name FROM users WHERE city = '北京'
 UNION ALL
 SELECT name FROM users WHERE city = '上海';
其他常用关键字
DISTINCT
- 解释:去除查询结果中的重复行。 
- 示例: - 1 
 2- -- 查询所有不重复的城市 
 SELECT DISTINCT city FROM users;
AS
- 解释:为列或表指定别名,简化查询语句。 
- 示例: - 1 
 2
 3
 4- -- 为表和列起别名 
 SELECT u.name AS 用户名, o.product_name AS 商品名
 FROM users u, orders o
 WHERE u.id = o.user_id;
MySQL 关键字执行顺序
MySQL 执行 SQL 语句时,会按照特定的逻辑顺序解析和执行关键字,而非语句中书写的顺序。理解这一顺序有助于优化查询和排查问题。以下是核心关键字的执行流程(从先到后)。
执行流程
- FROM
- 首先确定查询的数据源,即指定要操作的表(或视图、子查询等)。
- 若涉及多表连接(如 JOIN),会在此阶段进行表的关联,生成临时的 “连接结果集”。
- ON
- 针对多表连接(JOIN),ON用于指定表之间的关联条件,筛选符合条件的连接记录,仅作用于关联的表。
- JOIN
- 根据 JOIN类型(INNER JOIN、LEFT JOIN等),结合ON条件,生成最终的连接结果集(包含匹配的记录,左连接 / 右连接会保留主表未匹配的记录)。
- WHERE
- 对 FROM和JOIN生成的临时结果集进行筛选,保留满足条件的记录。
- 注意:WHERE中不能使用SELECT中定义的别名,也不能使用聚合函数(如SUM()、COUNT()),因为此时聚合尚未执行。
- GROUP BY
- 将 WHERE筛选后的结果集按照指定字段分组,相同分组的记录会被合并为一组。
- 分组后,后续的聚合函数(如 SUM、AVG)会基于每组数据计算结果。
- HAVING
- 对 GROUP BY分组后的结果进行二次筛选,可使用聚合函数(因分组已完成)。
- 与 WHERE的区别:HAVING作用于分组后的数据,WHERE作用于分组前的原始数据。
- SELECT
- 从前面步骤处理后的结果集中,提取指定的列(或计算字段、别名等)。
- 此时可以使用 AS定义别名,供后续步骤(如ORDER BY)使用,注意在mysql对此做了扩展,字段别名也允许在GROUP By + HAVING中使用。
- DISTINCT
- 对 SELECT提取的结果进行去重,保留唯一的记录(仅在指定DISTINCT时执行)。
- ORDER BY
- 按照指定字段(或表达式)对结果集进行排序(升序 ASC或降序DESC)。
- 可以使用 SELECT中定义的别名,也可以使用列的序号(不推荐,可读性差)。
- LIMIT/- OFFSET
- 最后对排序后的结果集进行截取,返回指定数量的记录(如分页查询)。
示例
以下 SQL 语句的书写顺序与执行顺序对比:
| 1 | SELECT dept, AVG(salary) AS avg_salary -- 7. SELECT(定义别名) | 
注意
- 别名使用范围:SELECT中定义的别名可在ORDER BY、HAVING中使用,但不能在WHERE中使用(因执行顺序在前,注意:标准sql别名也不允许在GROUP BY + HAVING中使用,只是mysql做了扩展而已: 有兴趣可研读 MySQL官网)。
- 聚合函数位置:聚合函数(如 SUM)只能在SELECT、HAVING中使用,不能在WHERE中使用(WHERE执行时未分组,无法聚合)。
- 效率影响:WHERE比HAVING更早执行,尽量用WHERE筛选原始数据以减少分组 / 聚合的计算量,提升效率。
DCL语句
MySQL的DCL语句(DCL,Data Control Language,数据控制语言)主要分为两类:用户管理语句和权限控制语句。以下是按分类列举的MySQL DCL语句,以及对可控制的权限内容的介绍和示例。
用户管理语句
- 查询用户
| 1 | SELECT * FROM mysql.user; | 
- 创建用户
| 1 | CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码'; | 
- 修改用户密码
| 1 | ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码'; | 
- 删除用户
| 1 | DROP USER '用户名'@'主机名'; | 
权限控制语句
- 查询权限
| 1 | SHOW GRANTS FOR '用户名'@'主机名'; | 
- 授予权限
| 1 | GRANT privileges ON database.table TO 'username'@'host' [WITH GRANT OPTION]; | 
- 撤销权限
| 1 | REVOKE privileges ON database.table FROM 'username'@'host'; |