前言
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
3ALTER TABLE 表名 ADD 字段名 数据类型 [默认值 非空约束] [COMMENT 注释];
# 示例
ALTER TABLE users ADD email VARCHAR(100);- 修改某字段的数据类型
1
2
3ALTER TABLE 表名 MODIFY 字段名 新数据类型 [默认值 非空约束] [COMMENT 注释];
# 示例
ALTER TABLE users MODIFY username VARCHAR(100) NOT NULL COMMENT '用户名';- 修改字段名和字段类型
1
2
3ALTER TABLE 表名 CHANGE 旧字段名 新字段名 新数据类型 [默认值 非空约束] [COMMENT 注释] ;
# 示例
ALTER TABLE users CHANGE nickname nick VARCHAR(30);- 删除某字段
1
2
3ALTER TABLE 表名 DROP 字段名;
# 示例
ALTER TABLE users DROP COLUMN email;- 修改表名
1
2
3ALTER 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'; |