Mysql常用语句汇总

Mysql语句分类

  • DDL: 数据定义语言,用来定义数据库对象(数据库、表、字段)
  • DML: 数据操作语言,用来对数据库表中的数据进行增删改
  • DQL: 数据查询语言,用来查询数据库中表的记录
  • DCL: 数据控制语言,用来创建数据库用户、控制数据库的控制权限

Mysql中的常用数据类型

数值类型

数据类型 说明与用途 占用空间 范围(有符号)

|-----------|------------------------|------|------------|
| TINYINT | 极小的整数,常用于布尔值或状态标志(0/1) | 1 字节 | -128 到 127 |

|------------|-----|------|------------------|
| SMALLINT | 小整数 | 2 字节 | -32,768 到 32,767 |

|-------------|-----|------|------------------------|
| MEDIUMINT | 中整数 | 3 字节 | -8,388,608 到 8,388,607 |

|-------------------|-----------|------|--------------------------------|
| INT / INTEGER | 常用整数型,最常见 | 4 字节 | -2,147,483,648 到 2,147,483,647 |

|----------|---------------------|------|---------------|
| BIGINT | 大整数,适合用于用户 ID、资金数据等 | 8 字节 | ±9 * 10¹⁸ 级别 |

|--------------|--------|------|----------------------|
| FLOAT(M,D) | 单精度浮点数 | 4 字节 | 小数位数有限,适用于非精确计算(如图形) |

|---------------|--------|------|------|
| DOUBLE(M,D) | 双精度浮点数 | 8 字节 | 精度更高 |

|----------------|--------------------------|--------|------------------|
| DECIMAL(M,D) | 精确小数,常用于财务数据,M 总长度,D 小数位 | 根据精度决定 | 精确存储十进制数,不存在浮点误差 |

字符串类型

数据类型 说明与用途 特点
CHAR(n) 固定长度字符串,适合长度固定的数据,如身份证号 占用固定空间,存储和查询速度快
VARCHAR(n) 可变长度字符串,适合大多数文本字段 节省空间,推荐使用
TEXT 长文本,最大 65,535 字节 不能设置默认值,适合长评论、文章等
TINYTEXT 最多 255 字节 适合很短的文本
MEDIUMTEXT 最多 16,777,215 字节 适合大段文章、日志
LONGTEXT 最多 4GB(4,294,967,295 字节) 超大文本,如小说、日志文件等
ENUM(...) 枚举类型,只允许特定值之一,如 ENUM('男','女') 存储为整数索引,节省空间,提高一致性
SET(...) 集合类型,允许多个选项的组合 通常用于表示多选项,如兴趣、标签等

日期与时间类型

数据类型 说明与用途 格式

|--------|-----|----------------|
| DATE | 仅日期 | 'YYYY-MM-DD' |

|--------|-----|--------------|
| TIME | 仅时间 | 'HH:MM:SS' |

|------------|--------------------|-------------------------|
| DATETIME | 日期和时间,推荐使用(不受时区影响) | 'YYYY-MM-DD HH:MM:SS' |

|-------------|----------------------------|------------|
| TIMESTAMP | 日期和时间,随时区自动调整,常用于记录创建/修改时间 | 同上,自动更新时间戳 |

|--------|-----------------|----------|
| YEAR | 仅年份(1901--2155) | 'YYYY' |

DDL(数据定义语言)

数据库操作

查询所有数据库:

sql 复制代码
SHOW DATABASES;

查询当前数据库:

sql 复制代码
SELECT DATABASE();
sql 复制代码
USE testdb;
SELECT DATABASE();
结果:
testdb

如果没有使用use选中任何数据库,执行该语句返回的是NULL

创建数据库:

sql 复制代码
CREATE DATABASE [IF NOT EXISTS] 数据库名
[DEFAULT CHARACTER SET 字符集]
[COLLATE 排序规则];

常见字符集

字符集名 描述与用途 支持字符范围 是否支持 Emoji
utf8mb4 推荐使用的多字节字符集,完全支持 Unicode,包括 Emoji 和扩展字符。 全 Unicode(最多 4 字节) ✅ 支持
utf8 早期的 UTF-8 兼容字符集,但不完全,最多只支持 3 字节,无法包含部分 Emoji。 基本多语言字符(最多 3 字节) ❌ 不支持
latin1 西欧语言用单字节字符集,占用少,性能高,但不支持多语言和 Emoji。 只支持 ISO 8859-1 字符 ❌ 不支持
gbk / gb2312 简体中文编码(GBK 扩展 GB2312),用于中国老系统,但不推荐用于新项目。 支持汉字及部分符号 ❌ 不支持
ascii 仅支持 0--127 的 ASCII 码字符,体积最小,适用于英文或标识符字段。 仅英文字母、数字和常用标点 ❌ 不支持

常见排序规则

注意:排序规则是字符集相关的 ,命名格式通常为 字符集_比较规则,如:utf8mb4_general_ci

排序规则名 所属字符集 描述 是否区分大小写 排序特性
utf8mb4_general_ci utf8mb4 通用型排序规则,不区分大小写,性能较高。 ❌ 不区分 快速但不完全 Unicode 正确
utf8mb4_unicode_ci utf8mb4 更严格的 Unicode 排序规则,处理国际字符更准确,略慢。 ❌ 不区分 遵循 Unicode 排序规则
utf8mb4_bin utf8mb4 使用二进制编码进行比较,区分大小写,适合精确比较。 ✅ 区分 精确但不符合自然语言排序
latin1_swedish_ci latin1 latin1 默认排序规则,原设计面向瑞典语(但已广泛用于默认场景)。 ❌ 不区分 一般用作 latin1 默认设置
utf8mb4_0900_ai_ci utf8mb4 MySQL 8.0 引入的新 Unicode 9.0 排序,ai 表示 accent-insensitive。 ❌ 不区分大小写/重音 更精细的 Unicode 支持
utf8mb4_0900_as_cs utf8mb4 区分大小写、区分重音的新排序规则,适用于严格比较。 ✅ 区分 完全精确比较

示例

sql 复制代码
CREATE DATABASE IF NOT EXISTS mydb
DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_general_ci;

如果 mydb 数据库还不存在,则创建它;
使用 utf8mb4 字符集(支持 Emoji 和更多语言);
使用 utf8mb4_general_ci 作为排序规则(不区分大小写的通用排序方式)。

删除数据库:

sql 复制代码
DROP DATABASE [ IF EXISTS ] 数据库名;

使用数据库:

sql 复制代码
USE 数据库名;

表操作

查询当前数据库所有表:

sql 复制代码
SHOW TABLES;

创建表:

sql 复制代码
CREATE TABLE 表名(
	字段1 字段1类型 [COMMENT 字段1注释],
	字段2 字段2类型 [COMMENT 字段2注释],
	字段3 字段3类型 [COMMENT 字段3注释],
	...
	字段n 字段n类型 [COMMENT 字段n注释]
)[ COMMENT 表注释 ];

最后一个字段后面没有逗号

查询表结构:

sql 复制代码
DESC 表名;

它会列出表中**每一列(字段)的:

  • 字段名

  • 类型(数据类型)

  • 是否允许 NULL

  • 键(如是否是主键、外键、索引)

  • 默认值

  • 其他额外信息(如 auto_increment

假设有这样一张表 users

sql 复制代码
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

查询结果会是这样的:

Field Type Null Key Default Extra
id int(11) NO PRI NULL auto_increment
username varchar(50) NO NULL
email varchar(100) YES NULL
created_at timestamp YES CURRENT_TIMESTAMP

查询指定表的建表语句:

sql 复制代码
SHOW CREATE TABLE 表名;

添加字段:

sql 复制代码
ALTER TABLE 表名 ADD 字段名 类型(长度) [COMMENT 注释] [约束];

ALTER TABLE emp ADD nickname varchar(20) COMMENT '昵称';

修改数据类型:

sql 复制代码
ALTER TABLE 表名 MODIFY 字段名 新数据类型(长度);

修改字段名和字段类型:

sql 复制代码
ALTER TABLE 表名 CHANGE 旧字段名 新字段名 类型(长度) [COMMENT 注释] [约束];

将emp表的nickname字段修改为username,类型为varchar(30)
ALTER TABLE emp CHANGE nickname username varchar(30) COMMENT '昵称';

删除字段:

sql 复制代码
ALTER TABLE 表名 DROP 字段名;

修改表名:

sql 复制代码
ALTER TABLE 表名 RENAME TO 新表名;

删除表:

sql 复制代码
DROP TABLE [IF EXISTS] 表名;

删除表,并重新创建该表:

sql 复制代码
TRUNCATE TABLE 表名;

DML(数据操作语言)

添加数据

指定字段:

sql 复制代码
INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (值1, 值2, ...);

全部字段:

sql 复制代码
INSERT INTO 表名 VALUES (值1, 值2, ...);

批量添加数据:

sql 复制代码
INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (值1, 值2, ...), (值1, 值2, ...), (值1, 值2, ...);
INSERT INTO 表名 VALUES (值1, 值2, ...), (值1, 值2, ...), (值1, 值2, ...);

注意事项

  • 字符串和日期类型数据应该包含在引号中
  • 插入的数据大小应该在字段的规定范围内

更新和删除数据

修改数据:

sql 复制代码
UPDATE 表名 SET 字段名1 = 值1, 字段名2 = 值2, ... [ WHERE 条件 ];

UPDATE emp SET name = 'Jack' WHERE id = 1;

删除数据:

sql 复制代码
DELETE FROM 表名 [ WHERE 条件 ];

DQL(数据查询语言)

基础查询

查询多个字段:

sql 复制代码
#查询指定字段
SELECT 字段1, 字段2, 字段3, ... FROM 表名;
#查询所有字段
SELECT * FROM 表名;

设置别名:

sql 复制代码
SELECT 字段1 [ AS 别名1 ], 字段2 [ AS 别名2 ], 字段3 [ AS 别名3 ], ... FROM 表名;
SELECT 字段1 [ 别名1 ], 字段2 [ 别名2 ], 字段3 [ 别名3 ], ... FROM 表名;

去除指定字段的重复记录:

sql 复制代码
SELECT DISTINCT 字段1, 字段2, ... FROM 表名;

条件查询

语法:

sql 复制代码
SELECT 字段列表 FROM 表名 WHERE 条件列表;

条件:

比较运算符 功能
> 大于
>= 大于等于
< 小于
<= 小于等于
= 等于
<> 或 != 不等于
BETWEEN ... AND ... 在某个范围内(含最小、最大值)
IN(...) 在in之后的列表中的值,多选一
LIKE 占位符 模糊匹配(_匹配单个字符,%匹配任意个字符)
IS NULL 是NULL
逻辑运算符 功能
AND 或 && 并且(多个条件同时成立)
OR 或 || 或者(多个条件任意一个成立)
NOT 或 ! 非,不是

示例:

sql 复制代码
-- 年龄等于30
select * from employee where age = 30;
-- 年龄小于30
select * from employee where age < 30;
-- 小于等于
select * from employee where age <= 30;
-- 没有身份证
select * from employee where idcard is null or idcard = '';
-- 有身份证
select * from employee where idcard;
select * from employee where idcard is not null;
-- 不等于
select * from employee where age != 30;
-- 年龄在20到30之间
select * from employee where age between 20 and 30;
select * from employee where age >= 20 and age <= 30;
-- 下面语句不报错,但查不到任何信息
select * from employee where age between 30 and 20;
-- 性别为女且年龄小于30
select * from employee where age < 30 and gender = '女';
-- 年龄等于25或30或35
select * from employee where age = 25 or age = 30 or age = 35;
select * from employee where age in (25, 30, 35);
-- 姓名为两个字
select * from employee where name like '__';
-- 身份证最后为X
select * from employee where idcard like '%X';

聚合查询(聚合函数)

常见聚合函数:

函数 功能
count 统计数量
max 最大值
min 最小值
avg 平均值
sum 求和

语法:

sql 复制代码
SELECT count(id) from employee where workaddress = "广东省";

分组查询

sql 复制代码
SELECT 字段列表 FROM 表名 [ WHERE 条件 ] GROUP BY 分组字段名 [ HAVING 分组后的过滤条件 ];

where 和 having 的区别:

  • 执行时机不同:where是分组之前进行过滤,不满足where条件不参与分组;having是分组后对结果进行过滤。
  • 判断条件不同:where不能对聚合函数进行判断,而having可以。

示例:

sql 复制代码
-- 根据性别分组,统计男性和女性数量(只显示分组数量,不显示哪个是男哪个是女)
select count(*) from employee group by gender;
-- 根据性别分组,统计男性和女性数量
select gender, count(*) from employee group by gender;
-- 根据性别分组,统计男性和女性的平均年龄
select gender, avg(age) from employee group by gender;
-- 年龄小于45,并根据工作地址分组
select workaddress, count(*) from employee where age < 45 group by workaddress;
-- 年龄小于45,并根据工作地址分组,获取员工数量大于等于3的工作地址
select workaddress, count(*) address_count from employee where age < 45 group by workaddress having address_count >= 3;

注意事项

  • 执行顺序:where > 聚合函数 > having
  • 分组之后,查询的字段一般为聚合函数和分组字段,查询其他字段无任何意义

排序查询

sql 复制代码
SELECT 字段列表 FROM 表名 ORDER BY 字段1 排序方式1, 字段2 排序方式2;

排序方式:

  • ASC: 升序(默认)
  • DESC: 降序

示例:

sql 复制代码
-- 根据年龄升序排序
SELECT * FROM employee ORDER BY age ASC;
SELECT * FROM employee ORDER BY age;
-- 两字段排序,根据年龄升序排序,入职时间降序排序
SELECT * FROM employee ORDER BY age ASC, entrydate DESC;

注意

如果是多字段排序,当第一个字段值相同时,才会根据第二个字段进行排序

分页查询

sql 复制代码
SELECT 字段列表 FROM 表名 LIMIT 起始索引, 查询记录数;

示例:

sql 复制代码
-- 查询第一页数据,展示10条
SELECT * FROM employee LIMIT 0, 10;
-- 查询第二页
SELECT * FROM employee LIMIT 10, 10;

注意

  • 起始索引从0开始,起始索引 = (查询页码 - 1) * 每页显示记录数
  • 分页查询是数据库的方言,不同数据库有不同实现,MySQL是LIMIT
  • 如果查询的是第一页数据,起始索引可以省略,直接简写 LIMIT 10

DQL执行顺序

FROM -> WHERE -> GROUP BY -> SELECT -> ORDER BY -> LIMIT

DCL(数据控制语言)

管理用户

查询用户:

sql 复制代码
mysql 是 MySQL 系统自带的数据库,不是我们平时建的用户数据库。
它内部保存着各种权限、用户、系统配置表,比如 user, db, tables_priv 等。
USE mysql;
查询 mysql 数据库中的 user 表,查看所有用户的完整信息。
SELECT * FROM user;

创建用户:

sql 复制代码
CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';

修改用户密码:

sql 复制代码
ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码';

删除用户:

sql 复制代码
DROP USER '用户名'@'主机名';

示例:

sql 复制代码
-- 创建用户test,只能在当前主机localhost访问
create user 'test'@'localhost' identified by '123456';
-- 创建用户test,能在任意主机访问
create user 'test'@'%' identified by '123456';
create user 'test' identified by '123456';
-- 修改密码
alter user 'test'@'localhost' identified with mysql_native_password by '1234';
-- 删除用户
drop user 'test'@'localhost';

权限控制

常用权限:

权限 说明
ALL, ALL PRIVILEGES 所有权限
SELECT 查询数据
INSERT 插入数据
UPDATE 修改数据
DELETE 删除数据
ALTER 修改表
DROP 删除数据库/表/视图
CREATE 创建数据库/表

查询权限:

sql 复制代码
SHOW GRANTS FOR '用户名'@'主机名';

授予权限:

sql 复制代码
GRANT 权限列表 ON 数据库名.表名 TO '用户名'@'主机名';

撤销权限:

sql 复制代码
REVOKE 权限列表 ON 数据库名.表名 FROM '用户名'@'主机名';

注意

  • 多个权限用逗号分隔
  • 授权时,数据库名和表名可以用 * 进行通配,代表所有

示例:

sql 复制代码
-- 创建数据库、创建用户、授权、查看权限、撤销权限

-- 1. 创建数据库(如果不存在)
CREATE DATABASE IF NOT EXISTS mydb
DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_general_ci;

-- 2. 创建用户 alice@localhost,密码为 'password123'
CREATE USER IF NOT EXISTS 'alice'@'localhost'
IDENTIFIED BY 'password123';

-- 3. 授权 alice 对 mydb 中所有表拥有 SELECT 和 INSERT 权限
GRANT SELECT, INSERT
ON mydb.* 
TO 'alice'@'localhost';

-- 4. 查看 alice 拥有的权限
SHOW GRANTS FOR 'alice'@'localhost';

-- 5. 撤销 alice 的 INSERT 权限
REVOKE INSERT 
ON mydb.* 
FROM 'alice'@'localhost';

-- 6. 再次查看 alice 的权限(验证撤权结果)
SHOW GRANTS FOR 'alice'@'localhost';

-- 7. (可选)删除 alice 用户
-- DROP USER 'alice'@'localhost';

函数

  • 字符串函数
  • 数值函数
  • 日期函数
  • 流程函数

字符串函数

常用函数:

函数 功能
CONCAT(s1, s2, ..., sn) 字符串拼接,将s1, s2, ..., sn拼接成一个字符串
LOWER(str) 将字符串全部转为小写
UPPER(str) 将字符串全部转为大写
LPAD(str, n, pad) 左填充,用字符串pad对str的左边进行填充,达到n个字符串长度
RPAD(str, n, pad) 右填充,用字符串pad对str的右边进行填充,达到n个字符串长度
TRIM(str) 去掉字符串头部和尾部的空格
SUBSTRING(str, start, len) 返回从字符串str从start位置起的len个长度的字符串
REPLACE(column, source, replace) 替换字符串

使用示例:

sql 复制代码
-- 拼接
SELECT CONCAT('Hello', 'World');
-- 小写
SELECT LOWER('Hello');
-- 大写
SELECT UPPER('Hello');
-- 左填充
SELECT LPAD('01', 5, '-');
-- 右填充
SELECT RPAD('01', 5, '-');
-- 去除空格
SELECT TRIM(' Hello World ');
-- 切片(起始索引为1)
SELECT SUBSTRING('Hello World', 1, 5);

数值函数

常见函数:

函数 功能
CEIL(x) 向上取整
FLOOR(x) 向下取整
MOD(x, y) 返回x/y的模
RAND() 返回0~1内的随机数
ROUND(x, y) 求参数x的四舍五入值,保留y位小数

示例:

sql 复制代码
-- 示例:MySQL 数学函数示例

-- 1. 向上取整:CEIL(x)
SELECT CEIL(4.2) AS ceil_result;  -- 返回 5

-- 2. 向下取整:FLOOR(x)
SELECT FLOOR(4.7) AS floor_result;  -- 返回 4

-- 3. 取模:MOD(x, y)
SELECT MOD(10, 3) AS mod_result;  -- 返回 1

-- 4. 生成随机数:RAND()
SELECT RAND() AS random_number;  -- 返回一个 0 到 1 之间的随机数

-- 5. 四舍五入:ROUND(x, y)
SELECT ROUND(4.567, 2) AS round_result;  -- 返回 4.57

日期函数

常用函数:

函数 功能
CURDATE() 返回当前日期
CURTIME() 返回当前时间
NOW() 返回当前日期和时间
YEAR(date) 获取指定date的年份
MONTH(date) 获取指定date的月份
DAY(date) 获取指定date的日期
DATE_ADD(date, INTERVAL expr type) 返回一个日期/时间值加上一个时间间隔expr后的时间值
DATEDIFF(date1, date2) 返回起始时间date1和结束时间date2之间的天数

示例:

sql 复制代码
-- 示例:MySQL 日期时间函数示例

-- 1. 返回当前日期:CURDATE()
SELECT CURDATE() AS current_date;

-- 2. 返回当前时间:CURTIME()
SELECT CURTIME() AS current_time;

-- 3. 返回当前日期和时间:NOW()
SELECT NOW() AS current_datetime;

-- 4. 获取指定日期的年份:YEAR(date)
SELECT YEAR('2025-05-02') AS year_of_date;  -- 返回 2025

-- 5. 获取指定日期的月份:MONTH(date)
SELECT MONTH('2025-05-02') AS month_of_date;  -- 返回 5

-- 6. 获取指定日期的日期部分:DAY(date)
SELECT DAY('2025-05-02') AS day_of_date;  -- 返回 2

-- 7. 返回一个日期加上时间间隔后的新日期:DATE_ADD(date, INTERVAL expr type)
SELECT DATE_ADD('2025-05-02', INTERVAL 10 DAY) AS new_date;  -- 返回 2025-05-12

-- 8. 返回两个日期之间的天数:DATEDIFF(date1, date2)
SELECT DATEDIFF('2025-05-02', '2025-04-22') AS date_diff;  -- 返回 10

流程函数

常用函数:

函数 功能
IF(value, t, f) 如果value为true,则返回t,否则返回f
IFNULL(value1, value2) 如果value1不为空,返回value1,否则返回value2
CASE WHEN [ val1 ] THEN [ res1 ] ... ELSE [ default ] END 如果val1为true,返回res1,... 否则返回default默认值
CASE [ expr ] WHEN [ val1 ] THEN [ res1 ] ... ELSE [ default ] END 如果expr的值等于val1,返回res1,... 否则返回default默认值

示例:

sql 复制代码
-- 示例:MySQL 条件判断函数示例

-- 1. IF(value, t, f):如果 value 为 true,则返回 t,否则返回 f
SELECT IF(5 > 3, 'True', 'False') AS if_example;  -- 返回 'True'

-- 2. IFNULL(value1, value2):如果 value1 不为空,返回 value1,否则返回 value2
SELECT IFNULL(NULL, 'Default Value') AS ifnull_example;  -- 返回 'Default Value'

-- 3. CASE WHEN [val1] THEN [res1] ... ELSE [default] END:如果 val1 为 true,返回 res1,... 否则返回 default 默认值
SELECT CASE 
           WHEN 5 > 3 THEN 'Greater' 
           WHEN 5 < 3 THEN 'Smaller' 
           ELSE 'Equal' 
       END AS case_when_example;  -- 返回 'Greater'

-- 4. CASE [expr] WHEN [val1] THEN [res1] ... ELSE [default] END:如果 expr 的值等于 val1,返回 res1,... 否则返回 default 默认值
SELECT CASE 5 
           WHEN 3 THEN 'Three' 
           WHEN 5 THEN 'Five' 
           ELSE 'Other' 
       END AS case_example;  -- 返回 'Five'

约束

分类:

约束 描述 关键字
非空约束 限制该字段的数据不能为null NOT NULL
唯一约束 保证该字段的所有数据都是唯一、不重复的 UNIQUE
主键约束 主键是一行数据的唯一标识,要求非空且唯一 PRIMARY KEY
默认约束 保存数据时,如果未指定该字段的值,则采用默认值 DEFAULT
检查约束(8.0.1版本后) 保证字段值满足某一个条件 CHECK
外键约束 用来让两张图的数据之间建立连接,保证数据的一致性和完整性 FOREIGN KEY

约束是作用于表中字段上的,可以再创建表/修改表的时候添加约束。

常用约束

约束条件 关键字
主键 PRIMARY KEY
自动增长 AUTO_INCREMENT
不为空 NOT NULL
唯一 UNIQUE
逻辑条件 CHECK
默认值 DEFAULT

示例:

sql 复制代码
create table user(
	id int primary key auto_increment,
	name varchar(10) not null unique,
	age int check(age > 0 and age < 120),
	status char(1) default '1',
	gender char(1)
);

外键约束

外键(Foreign Key)是数据库中用来确保数据一致性和完整性的一种约束。外键用于在两个表之间建立关联关系,使得一个表的某一列(或一组列)指向另一个表的主键(Primary Key)或唯一键(Unique Key)。外键的作用是确保表之间的数据关系有效,同时避免因数据的错误操作导致不一致的情况。

添加外键:

sql 复制代码
CREATE TABLE 表名(
	字段名 字段类型,
	...
	[CONSTRAINT] [外键名称] FOREIGN KEY(外键字段名) REFERENCES 主表(主表列名)
);
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名) REFERENCES 主表(主表列名);

-- 例子
alter table emp add constraint fk_emp_dept_id foreign key(dept_id) references dept(id);

删除外键:

sql 复制代码
ALTER TABLE 表名 DROP FOREIGN KEY 外键名;

删除/更新行为:

行为 说明
NO ACTION 当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新(与RESTRICT一致)
RESTRICT 当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新(与NO ACTION一致)
CASCADE 当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则也删除/更新外键在子表中的记录
SET NULL 当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则设置子表中该外键值为null(要求该外键允许为null)
SET DEFAULT 父表有变更时,子表将外键设为一个默认值(Innodb不支持)

更改删除/更新行为:

sql 复制代码
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段) REFERENCES 主表名(主表字段名) ON UPDATE 行为 ON DELETE 行为;

示例:

创建 students

sql 复制代码
CREATE TABLE students (
    id INT AUTO_INCREMENT PRIMARY KEY,  -- 学生ID,自增长主键
    name VARCHAR(100) NOT NULL,          -- 学生姓名
    age INT                              -- 学生年龄
);

students 表存储每个学生的基本信息,并且包含一个主键 id,用于唯一标识每个学生。

创建 enrollments

sql 复制代码
CREATE TABLE enrollments (
    enrollment_id INT AUTO_INCREMENT PRIMARY KEY,  -- 选课记录ID
    student_id INT,                                -- 学生ID(外键)
    course_name VARCHAR(100),                      -- 课程名称
    enrollment_date DATE,                          -- 选课日期
    FOREIGN KEY (student_id) REFERENCES students(id)  -- 外键约束
);

enrollments 表存储学生的选课信息,其中包含一个外键 student_id,它引用 students 表的 id 字段。这意味着每一条选课记录都必须对应一个存在的学生。

在这个示例中:

  • students 表中的 id 是主键,确保每个学生都是唯一的。

  • enrollments 表中的 student_id 是外键,引用 students 表中的 id。这表示 enrollments 表中的每一条记录都应该关联一个有效的学生。

我们向这两个表中插入一些数据:

sql 复制代码
向 students 表插入数据
INSERT INTO students (name, age) VALUES
('Alice', 20),
('Bob', 22),
('Charlie', 21);

向 enrollments 表插入数据
INSERT INTO enrollments (student_id, course_name, enrollment_date) VALUES
(1, 'Mathematics', '2025-05-01'),  -- Alice 选修了数学
(2, 'Computer Science', '2025-05-01'),  -- Bob 选修了计算机科学
(1, 'Physics', '2025-05-02');  -- Alice 选修了物理

外键约束的作用

如果你尝试在 enrollments 表中插入一个没有对应 students 表中 idstudent_id,例如 student_id = 4,数据库会抛出错误,因为外键约束确保了 enrollments 表中的每个学生必须在 students 表中存在。

sql 复制代码
-- 这会报错,因为 student_id 4 不存在
INSERT INTO enrollments (student_id, course_name, enrollment_date) VALUES (4, 'Biology', '2025-05-03');

如果你删除了 students 表中的某个学生,例如删除 Aliceid = 1),那么如果没有适当的外键约束(如 ON DELETE CASCADE),删除操作会失败,因为外键约束要求必须先删除子表中的关联数据。

sql 复制代码
-- 删除 Alice(id = 1)将失败,除非外键添加了级联删除
DELETE FROM students WHERE id = 1;

多表查询

多表关系

  • 一对多(多对一)
  • 多对多
  • 一对一

一对一

案例:用户与用户详情

关系:一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他详情字段放在另一张表中,以提升操作效率

实现:在任意一方加入外键,关联另外一方的主键,并且设置外键为唯一的(UNIQUE)

sql 复制代码
-- 用户主表(基础信息表)
CREATE TABLE user (
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID',
    username VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名',
    email VARCHAR(100) NOT NULL UNIQUE COMMENT '电子邮箱',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户基础表';

-- 用户详情表(扩展字段表)
CREATE TABLE user_detail (
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
    user_id INT NOT NULL UNIQUE COMMENT '关联的用户ID(外键唯一,确保一对一)',
    real_name VARCHAR(100) DEFAULT NULL COMMENT '真实姓名',
    address TEXT COMMENT '地址',
    phone VARCHAR(20) COMMENT '手机号',
    birthday DATE COMMENT '生日',
    FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户详情表';

一对多

案例:部门与员工

关系:一个部门对应多个员工,一个员工对应一个部门

实现:在多的一方建立外键,指向一的一方的主键

sql 复制代码
-- 部门表(Department)
CREATE TABLE department (
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '部门ID',
    name VARCHAR(100) NOT NULL UNIQUE COMMENT '部门名称',
    location VARCHAR(100) DEFAULT NULL COMMENT '部门所在地'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='部门表';

-- 员工表(Employee)
CREATE TABLE employee (
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '员工ID',
    name VARCHAR(100) NOT NULL COMMENT '员工姓名',
    email VARCHAR(100) UNIQUE COMMENT '员工邮箱',
    hire_date DATE COMMENT '入职日期',
    department_id INT NOT NULL COMMENT '所属部门ID',
    FOREIGN KEY (department_id) REFERENCES department(id) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工表';

多对多

案例:学生与课程

关系:一个学生可以选多门课程,一门课程也可以供多个学生选修

实现:建立第三张中间表,中间表至少包含两个外键,分别关联两方主键

sql 复制代码
-- 学生表(Student)
CREATE TABLE student (
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '学生ID',
    name VARCHAR(100) NOT NULL COMMENT '学生姓名',
    enrollment_year INT COMMENT '入学年份'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生表';

-- 课程表(Course)
CREATE TABLE course (
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '课程ID',
    name VARCHAR(100) NOT NULL COMMENT '课程名称',
    credit INT DEFAULT 1 COMMENT '学分'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='课程表';

-- 中间表:学生-课程关系表(Student_Course)
CREATE TABLE student_course (
    student_id INT NOT NULL COMMENT '学生ID',
    course_id INT NOT NULL COMMENT '课程ID',
    selected_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '选课时间',
    PRIMARY KEY (student_id, course_id),
    FOREIGN KEY (student_id) REFERENCES student(id) ON DELETE CASCADE,
    FOREIGN KEY (course_id) REFERENCES course(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生与课程关联表';

多表查询

合并查询(笛卡尔积,会展示所有组合结果):

sql 复制代码
select * from employee, dept;

笛卡尔积:两个集合A集合和B集合的所有组合情况(在多表查询时,需要消除无效的笛卡尔积)

消除无效笛卡尔积:

sql 复制代码
select * from employee, dept where employee.dept = dept.id;

内连接查询 INNER JOIN

内连接查询的是两张表交集的部分

隐式内连接:

sql 复制代码
SELECT 字段列表 FROM 表1, 表2 WHERE 条件 ...;

显式内连接:

sql 复制代码
SELECT 字段列表 FROM 表1 [ INNER ] JOIN 表2 ON 连接条件 ...;

显式性能比隐式高

sql 复制代码
-- 查询员工姓名,及关联的部门的名称
-- 隐式
select e.name, d.name from employee as e, dept as d where e.dept = d.id;
-- 显式
select e.name, d.name from employee as e inner join dept as d on e.dept = d.id;

dept示例数据:
id	name
1	技术部
2	人事部
3	市场部
employee示例数据:
id	name	dept
1	张三	1
2	李四	2
3	王五	1
查询结果为:
e.name	d.name
张三	技术部
李四	人事部
王五	技术部

外连接查询

左外连接:LEFT JOIN

查询左表所有数据,以及两张表交集部分数据

会返回左表(表1)中的所有记录,如果右表(表2)中有匹配的,就一起返回;没有匹配的,右表的字段为 NULL。

sql 复制代码
SELECT 字段列表 FROM 表1 LEFT [ OUTER ] JOIN 表2 ON 条件 ...;
右外连接:RIGHT JOIN

查询右表所有数据,以及两张表交集部分数据

会返回右表(表2)中的所有记录,如果左表(表1)中有匹配的,就一起返回;没有匹配的,左表的字段为 NULL。

sql 复制代码
SELECT 字段列表 FROM 表1 RIGHT [ OUTER ] JOIN 表2 ON 条件 ...;
sql 复制代码
-- 左
select e.*, d.name from employee as e left outer join dept as d on e.dept = d.id;
select d.name, e.* from dept d left outer join emp e on e.dept = d.id;  -- 这条语句与下面的语句效果一样
-- 右
select d.name, e.* from employee as e right outer join dept as d on e.dept = d.id;


员工表 employee:
id	name	dept
1	张三	1
2	李四	2
3	王五	3
部门表 dept:
id	name
1	技术部
2	人事部
4	财务部

LEFT JOIN,从员工出发,查询结果
id	name	dept	dept_name
1	张三	1	    技术部
2	李四	2	    人事部
3	王五	3	     NULL
LEFT JOIN,从部门出发,查询结果
dept_name	id	 name	dept
技术部	    1	 张三	 1
人事部	    2	 李四	 2
财务部	   NULL	 NULL	NULL
左连接可以查询到没有dept的employee,右连接可以查询到没有employee的dept

自连接查询

当前表与自身的连接查询,自连接必须使用表别名

sql 复制代码
SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件 ...;

自连接查询,可以是内连接查询,也可以是外连接查询

sql 复制代码
员工表 employee:
id	name	manager
1	张三	NULL
2	李四	 1
3	王五	 2
4	赵六	NULL
张三和赵六没有领导(manager 字段为 NULL)
李四的领导是张三(manager 字段是 1,即张三的 id)
王五的领导是李四(manager 字段是 2,即李四的 id)

使用 INNER JOIN(默认的隐式连接):
SELECT a.name AS 员工, b.name AS 领导
FROM employee a, employee b
WHERE a.manager = b.id;
结果:
员工	领导
李四	张三
王五	李四

使用 LEFT JOIN(显式连接)
SELECT a.name AS 员工, b.name AS 领导
FROM employee a
LEFT JOIN employee b ON a.manager = b.id;
结果:
员工	领导
张三	NULL
李四	张三
王五	李四
赵六	NULL

联合查询 union, union all

把多次查询的结果合并,形成一个新的查询集

sql 复制代码
SELECT 字段列表 FROM 表A ...
UNION [ALL]
SELECT 字段列表 FROM 表B ...

注意

  • UNION ALL 会有重复结果,UNION 不会
  • 联合查询比使用or效率高,不会使索引失效

子查询

SQL语句中嵌套SELECT语句,称谓嵌套查询,又称子查询。

sql 复制代码
SELECT * FROM t1 WHERE column1 = ( SELECT column1 FROM t2);

子查询外部的语句可以是 INSERT / UPDATE / DELETE / SELECT 的任何一个

根据子查询结果可以分为:

  • 标量子查询(子查询结果为单个值)
  • 列子查询(子查询结果为一列)
  • 行子查询(子查询结果为一行)
  • 表子查询(子查询结果为多行多列)

根据子查询位置可分为:

  • WHERE 之后
  • FROM 之后
  • SELECT 之后
标量子查询

子查询返回的结果是单个值(数字、字符串、日期等)。

常用操作符:- < > > >= < <=

sql 复制代码
部门表 dept:
id	name
1	技术部
2	人事部
3	财务部
4	销售部
员工表 employee:
id	name	dept    entrydate
1	张三	 4	   2021-01-01
2	李四	 4	   2022-03-15
3	王五	 2	   2020-06-10
4	赵六	 1 	   2023-02-01
5	钱七	 4	   2024-07-01

-- 查询销售部所有员工
SELECT * FROM employee 
WHERE dept = (
    SELECT id FROM dept WHERE name = '销售部'
);
结果:
id	name	dept	entrydate
1	张三	 4	   2021-01-01
2	李四	 4	   2022-03-15
5	钱七	 4	   2024-07-01
-- 查询在"李四"入职之后的员工信息
SELECT * FROM employee 
WHERE entrydate > (
    SELECT entrydate FROM employee WHERE name = '李四'
);
结果:
id	name	dept	entrydate
4	赵六	1	2023-02-01
5	钱七	4	2024-07-01
列子查询

返回的结果是一列(可以是多行)。

常用操作符:

操作符 描述
IN 在指定的集合范围内,多选一
NOT IN 不在指定的集合范围内
ANY 子查询返回列表中,有任意一个满足即可
SOME 与ANY等同,使用SOME的地方都可以使用ANY
ALL 子查询返回列表的所有值都必须满足
sql 复制代码
部门表 dept:
id	name
1	销售部
2	市场部
3	财务部
4	研发部
员工表 employee:
id	name	dept	salary	entrydate
1	张三	1	6000	2021-01-01
2	李四	2	6500	2021-03-10
3	王五	3	7000	2022-06-01
4	赵六	3	7200	2023-02-20
5	钱七	4	8000	2022-09-10
6	孙八	4	8500	2023-10-01
7	周九	1	5000	2021-12-12

-- 查询销售部和市场部的所有员工信息
SELECT * FROM employee 
WHERE dept IN (
    SELECT id FROM dept 
    WHERE name = '销售部' OR name = '市场部'
);
解析:
子查询得到:dept.id in (1, 2)
所以等价于 WHERE dept IN (1, 2)
结果:
id	name  dept	salary	entrydate
1	张三	1	 6000	2021-01-01
2	李四	2	 6500	2021-03-10
7	周九	1	 5000	2021-12-12

-- 查询比财务部所有人工资都高的员工信息
SELECT * FROM employee 
WHERE salary > ALL (
    SELECT salary FROM employee 
    WHERE dept = (
        SELECT id FROM dept WHERE name = '财务部'
    )
);
解析:
财务部员工工资:7000(王五)、7200(赵六)
所以条件是 salary > 7200
结果:
id	name  dept salary	entrydate
5	钱七	4	8000	2022-09-10
6	孙八	4	8500	2023-10-01
行子查询

返回的结果是一行(可以是多列)。

常用操作符:=, <, >, IN, NOT IN

sql 复制代码
-- 查询与xxx的薪资及直属领导相同的员工信息
select * from employee where (salary, manager) = (12500, 1);
select * from employee where (salary, manager) = (select salary, manager from employee where name = 'xxx');

员工表 employee:
id	name    salary	manager	dept
1	张三	10000	NULL	 1
2	李四	12000	 1	     2
3	王五	12500	 1	     2
4	赵六	12500	 1	     3
5	钱七	12500	 2	     3
6	xxx	    12500	 1	     4
7	孙八	13000	 1	     4

-- 指定薪资和直属领导的组合
SELECT * FROM employee 
WHERE (salary, manager) = (12500, 1);
说明:
直接指定条件 (salary = 12500 AND manager = 1)
查询结果:
id	name	salary	manager	dept
3	王五	12500	   1	 2
4	赵六	12500	   1	 3
6	xxx	    12500	   1	 4

-- 查找与 xxx 的薪资和直属领导相同的员工信息
SELECT * FROM employee 
WHERE (salary, manager) = (
    SELECT salary, manager FROM employee WHERE name = 'xxx'
);
说明:
子查询得到 (12500, 1)
实际效果等同于前面一条语句
查询结果:
id	name	salary	manager	 dept
3	王五	12500	  1       2
4	赵六	12500	  1	      3
6	xxx	    12500	  1	      4
表子查询

返回的结果是多行多列

常用操作符:IN

sql 复制代码
员工表 employee:
id	name	 job	salary	entrydate	dept
1	张三	程序员	10000	2005-06-01	 1
2	李四	程序员	12000	2006-02-01	 2
3	xxx1	设计师	12500	2007-03-15	 2
4	xxx2	程序员	10000	2008-04-20	 3
5	赵六	程序员	10000	2009-05-10	 1
6	钱七	设计师	12500	2010-06-01	 2
7	孙八	产品经理	14000	2011-07-01	 3
部门表 dept:
id	 name
1	开发部
2	设计部
3	产品部

-- 查询与 xxx1、xxx2 的职位和薪资相同的员工
SELECT * FROM employee 
WHERE (job, salary) IN (
  SELECT job, salary FROM employee WHERE name = 'xxx1' OR name = 'xxx2'
);
说明:
xxx1 的 (job, salary) 是 (设计师, 12500)
xxx2 的 (job, salary) 是 (程序员, 10000)
所以匹配两个组合的员工
id	name	job	    salary	entrydate	dept
1	张三	程序员	10000	2005-06-01	 1
4	xxx2	程序员	10000	2008-04-20	 3
5	赵六	程序员	10000	2009-05-10	 1
3	xxx1	设计师	12500	2007-03-15	 2
6	钱七	设计师	12500	2010-06-01	 2

事务

事务是一组操作的集合,事务会把所有操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。

一句话:事务就是一组必须"要么全做完,要么一个也不做"的数据库操作。

比如:银行转账操作 ------ 张三转账给李四 1000 元,不能出现张三的钱扣了但李四没收到的情况。

示例:

sql 复制代码
-- 1. 查询张三账户余额
select * from account where name = '张三';
-- 2. 将张三账户余额-1000
update account set money = money - 1000 where name = '张三';
-- 此语句出错后张三钱减少但是李四钱没有增加
模拟sql语句错误
-- 3. 将李四账户余额+1000
update account set money = money + 1000 where name = '李四';

这时如果没有事务:

  • 张三的钱就被扣了

  • 李四的钱却没有增加

  • 钱"凭空消失",数据错误了!

所以必须让这步操作打包成一组事务,要么一起成功、要么一起失败回滚。

如何使用事务?

事务基本操作

开启事务:

sql 复制代码
START TRANSACTION 或 BEGIN TRANSACTION;

提交事务:

sql 复制代码
COMMIT;

回滚事务:

sql 复制代码
ROLLBACK;

方式一:关闭自动提交(手动控制提交)

数据库默认执行完一条语句就自动提交(也就是每条语句都是一个小事务)。要实现多个语句作为一组事务,需要手动控制。

sql 复制代码
-- 1. 查看当前是否是自动提交模式(1 表示开启自动提交)
SELECT @@AUTOCOMMIT;

-- 2. 关闭自动提交(设置为 0)
SET @@AUTOCOMMIT = 0;

-- 3. 执行一组事务操作
select * from account where name = '张三';
update account set money = money - 1000 where name = '张三';
update account set money = money + 1000 where name = '李四';

-- 4. 提交事务(保存操作)
COMMIT;

-- 如果发现中间出错了,可以撤回
ROLLBACK;

方式二:用 START TRANSACTION 开启事务

这种方式更清晰、推荐用于正式开发。

sql 复制代码
START TRANSACTION;  -- 或者用 BEGIN;

-- 一组操作(作为一个事务)
SELECT * FROM account WHERE name = '张三';
UPDATE account SET money = money - 1000 WHERE name = '张三';
UPDATE account SET money = money + 1000 WHERE name = '李四';

COMMIT;  -- 提交成功,保存所有操作
-- ROLLBACK;  -- 如果失败,用这个撤销

事务的四大特性:

  • 原子性(Atomicity):事务是不可分割的最小操作但愿,要么全部成功,要么全部失败
  • 一致性(Consistency):事务完成时,必须使所有数据都保持一致状态
  • 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行
  • 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的

并发事务:

描述
脏读 一个事务读到另一个事务还没提交的数据
不可重复读 一个事务先后读取同一条记录,但两次读取的数据不同
幻读 一个事务按照条件查询数据时,没有对应的数据行,但是再插入数据时,又发现这行数据已经存在

这三个问题的详细演示:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=55cd

并发事务隔离级别:

离级别 脏读 不可重复读 幻读
Read uncommitted
Read committed ×
Repeatable Read(默认) × ×
Serializable × × ×
  • √表示在当前隔离级别下该问题会出现
  • Serializable 性能最低;Read uncommitted 性能最高,数据安全性最差

查看事务隔离级别:

sql 复制代码
SELECT @@TRANSACTION_ISOLATION;

设置事务隔离级别:

sql 复制代码
SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE };

SESSION 是会话级别,表示只针对当前会话有效,GLOBAL 表示对所有会话有效

  • SESSION只影响当前会话(当前连接)的设置,别人不受影响。

  • GLOBAL影响整个数据库系统的设置,所有连接都会受到影响。

什么是"会话"?

你每次用 MySQL 客户端、Navicat、Java 程序、Python 脚本 连接数据库,都会建立一个 "会话(SESSION)",也叫一个"连接"。

每个连接之间是 相互独立的,就像每个人登录网站时都是自己的一套账号环境。

示例:AUTOCOMMIT 设置

sql 复制代码
SELECT @@AUTOCOMMIT;        -- 查看当前会话的自动提交设置
SELECT @@GLOBAL.AUTOCOMMIT; -- 查看全局的自动提交设置
sql 复制代码
SET SESSION AUTOCOMMIT = 0;
你自己这一次连接变成了"手动提交事务",但别人连进来仍然是"自动提交"。

SET GLOBAL AUTOCOMMIT = 0;
所有新连接进来的用户,默认都是"手动提交事务"状态了。

注意:GLOBAL 设置完后只影响新连接的会话,老连接不会变。

相关推荐
陆少枫3 小时前
MySQL基础关键_007_DQL 练习
数据库·mysql
千月落5 小时前
ClickHouse副本集群
服务器·数据库·clickhouse
找不到、了5 小时前
聊聊对Mysql的理解
数据库·mysql
kngines5 小时前
【PostgreSQL数据分析实战:从数据清洗到可视化全流程】4.2 数据类型转换(CAST函数/自定义函数)
数据库·postgresql·数据分析·filter·自定义函数·cte
半桶水专家7 小时前
使用frpc链接内网的mysql
数据库·mysql·adb
264玫瑰资源库7 小时前
网狐旗舰大联盟组件源码私测笔记:结构分层、UI重构与本地实操全流程
java·前端·数据库·笔记·ui·重构
KaiwuDB8 小时前
KaiwuDB X 遨博智能 | 构建智能产线监测管理新系统
大数据·数据库·kaiwudb·分布式多模数据库
程序猿不脱发29 小时前
mysql中int(1) 和 int(10) 有什么区别?
数据库·mysql
Themberfue9 小时前
Redis ⑨-Jedis | Spring Redis
java·数据库·redis·sql·spring·缓存