一. 函数
在 MySQL 中,函数是用于对数据进行特定处理或计算的工具,根据作用范围和返回结果的不同,主要分为单行函数和聚合函数(又称分组函数)。以下是详细介绍:
1.单行函数
单行函数对每一行数据单独处理,每行返回一个结果,常见类型包括字符串函数、数值函数、日期函数、流程控制函数等。
1.) 字符串函数
用于处理字符串类型数据,常见函数如下:
函数 | 功能描述 | 示例 | 结果 |
---|---|---|---|
CONCAT(str1, str2,...) |
拼接多个字符串(若有一个参数为NULL ,结果为NULL ) |
CONCAT('MySQL', ' ', '函数') |
MySQL 函数 |
CONCAT_WS(sep, str1, str2,...) |
用分隔符sep 拼接字符串(忽略NULL ) |
CONCAT_WS('-', '2023', '10', '05') |
2023-10-05 |
LENGTH(str) |
返回字符串的字节数(受字符集影响,UTF-8 中汉字占 3 字节) | LENGTH('中国') |
6 |
CHAR_LENGTH(str) |
返回字符串的字符数(与字符集无关) | CHAR_LENGTH('中国') |
2 |
UPPER(str) |
将字符串转为大写 | UPPER('mysql') |
MYSQL |
LOWER(str) |
将字符串转为小写 | LOWER('MYSQL') |
mysql |
SUBSTRING(str, pos, len) |
从pos 位置(起始为 1)截取长度为len 的子串(省略len 则取到末尾) |
SUBSTRING('HelloWorld', 7, 3) |
orl |
TRIM(str) |
去除字符串两端的空格(TRIM(指定字符 FROM str) 可去除指定字符) |
TRIM(' MySQL ') |
MySQL |
REPLACE(str, old, new) |
将str 中所有old 子串替换为new |
REPLACE('abc123abc', 'abc', 'x') |
x123x |
INSTR(str, substr) |
返回substr 在str 中首次出现的位置(无则返回 0) |
INSTR('abcde', 'cd') |
3 |
2.) 数值函数
用于数值计算,常见函数如下:
函数 | 功能描述 | 示例 | 结果 |
---|---|---|---|
ABS(x) |
返回x 的绝对值 |
ABS(-10) |
10 |
CEIL(x) |
向上取整(返回大于等于x 的最小整数) |
CEIL(3.2) |
4 |
FLOOR(x) |
向下取整(返回小于等于x 的最大整数) |
FLOOR(3.8) |
3 |
ROUND(x, d) |
四舍五入保留d 位小数(d 默认 0) |
ROUND(3.1415, 2) |
3.14 |
MOD(x, y) |
返回x 除以y 的余数(同x % y ) |
MOD(10, 3) |
1 |
POWER(x, y) |
返回x 的y 次方 |
POWER(2, 3) |
8 |
SQRT(x) |
返回x 的平方根 |
SQRT(16) |
4 |
3. )日期函数
用于处理日期和时间类型数据,常见函数如下:
函数 | 功能描述 | 示例 | 结果(假设当前时间为 2023-10-05 15:30:00) |
---|---|---|---|
NOW() |
返回当前日期和时间(YYYY-MM-DD HH:MM:SS ) |
NOW() |
2023-10-05 15:30:00 |
CURDATE() |
返回当前日期(YYYY-MM-DD ) |
CURDATE() |
2023-10-05 |
CURTIME() |
返回当前时间(HH:MM:SS ) |
CURTIME() |
15:30:00 |
YEAR(date) |
提取日期中的年份 | YEAR('2023-10-05') |
2023 |
MONTH(date) |
提取日期中的月份 | MONTH('2023-10-05') |
10 |
DAY(date) |
提取日期中的日 | DAY('2023-10-05') |
5 |
HOUR(time) |
提取时间中的小时 | HOUR('15:30:00') |
15 |
DATEDIFF(date1, date2) |
返回date1 - date2 的天数差 |
DATEDIFF('2023-10-05', '2023-10-01') |
4 |
DATE_ADD(date, INTERVAL expr unit) |
给日期添加指定时间间隔(unit :YEAR、MONTH、DAY 等) |
DATE_ADD('2023-10-05', INTERVAL 3 DAY) |
2023-10-08 |
DATE_FORMAT(date, format) |
将日期按format 格式转换为字符串(%Y 年、%m 月、%d 日等) |
DATE_FORMAT('2023-10-05', '%Y年%m月%d日') |
2023-10-05 |
4. )流程控制函数
用于实现条件判断逻辑,类似编程语言中的if-else
:
函数 | 功能描述 | 示例 | 结果 |
---|---|---|---|
IF(expr, v1, v2) |
若expr 为真(非 0/NULL),返回v1 ,否则返回v2 |
IF(10 > 5, '是', '否') |
是 |
IFNULL(v1, v2) |
若v1 不为NULL ,返回v1 ,否则返回v2 (处理 NULL 值) |
IFNULL(NULL, '空值') |
空值 |
CASE |
多条件判断(类似switch-case ) |
CASE 分数<br>WHEN 90 THEN '优秀'<br>WHEN 80 THEN '良好'<br>ELSE '及格'<br>END |
(根据分数返回对应等级) |
2.聚合函数(分组函数)
聚合函数用于对一组数据进行汇总计算,返回单一结果,通常与GROUP BY
配合使用(若不分组,则默认对全表数据聚合)。常见聚合函数如下:
函数 | 功能描述 | 示例(表students 含字段score ) |
结果 |
---|---|---|---|
COUNT(expr) |
统计非NULL 值的数量(COUNT(*) 统计所有行,包括NULL ) |
COUNT(score) |
分数非空的行数 |
SUM(expr) |
计算数值型字段的总和 | SUM(score) |
所有分数的总和 |
AVG(expr) |
计算数值型字段的平均值 | AVG(score) |
分数的平均值 |
MAX(expr) |
求字段的最大值 | MAX(score) |
最高分数 |
MIN(expr) |
求字段的最小值 | MIN(score) |
最低分数 |
注意事项:
聚合函数会自动忽略
NULL
值(COUNT(*)
除外)。若使用聚合函数时未加
GROUP BY
,则整个表视为一组。SELECT
子句中若有聚合函数,其他非聚合字段需出现在GROUP BY
中(避免歧义)。
3.函数使用示例
字符串拼接与日期格式化:
SELECT CONCAT('姓名:', name) AS 姓名, DATE_FORMAT(birthday, '%Y年%m月%d日') AS 生日 FROM students;
聚合函数统计:
SELECT COUNT(*) AS 总人数, AVG(score) AS 平均分, MAX(score) AS 最高分 FROM students;
条件判断:
SELECT name AS 姓名, score AS 分数, CASE WHEN score >= 90 THEN '优秀' WHEN score >= 80 THEN '良好' ELSE '及格' END AS 等级 FROM students;
二. 约束
在 MySQL 中,约束(Constraints)是用于限制表中数据的规则,确保数据的完整性、一致性和准确性。常见的 MySQL 约束包括以下几种:
主键约束(PRIMARY KEY)
唯一标识表中的每条记录
不能有重复值,且不能为 NULL
一个表只能有一个主键
CREATE TABLE students ( id INT PRIMARY KEY, name VARCHAR(50) );
外键约束(FOREIGN KEY)
用于关联两个表,确保参照完整性
外键列的值必须匹配另一个表的主键列的值或为 NULL
CREATE TABLE orders ( order_id INT PRIMARY KEY, student_id INT, FOREIGN KEY (student_id) REFERENCES students(id) );
唯一约束(UNIQUE)
确保列中的所有值都是唯一的
允许 NULL 值,但 NULL 只能出现一次
CREATE TABLE users ( id INT PRIMARY KEY, email VARCHAR(100) UNIQUE );
非空约束(NOT NULL)
确保列不能包含 NULL 值
CREATE TABLE employees ( id INT PRIMARY KEY, name VARCHAR(50) NOT NULL, age INT );
检查约束(CHECK)
确保列中的值满足指定的条件
MySQL 8.0.16 及以上版本支持
CREATE TABLE products ( id INT PRIMARY KEY, price DECIMAL(10,2) CHECK (price > 0) );
默认约束(DEFAULT)
为列指定默认值,如果插入数据时未指定该列的值,则使用默认值
CREATE TABLE customers ( id INT PRIMARY KEY, country VARCHAR(50) DEFAULT 'China' );
三. 多表查询
多表查询是数据库操作中常用的技术,用于从两个或多个相关联的表中获取数据。在 MySQL 中,实现多表查询主要有以下几种方式:
多表关系
1. 一对一关系(One-to-One)
特点:两个表中的记录一一对应,一个表的一条记录只能与另一个表的一条记录关联。
适用场景:常用于拆分表结构(如将用户基本信息和详细信息分开存储),或关联具有强归属关系的数据。
实现方式:在任意一个表中添加外键,关联另一个表的主键,并设置外键为唯一(
UNIQUE
)。
-- 示例:用户表(基本信息) CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL, email VARCHAR(100) UNIQUE NOT NULL ); -- 用户详情表(一对一关联 users 表) CREATE TABLE user_profiles ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT UNIQUE NOT NULL, -- 唯一外键,确保一对一 real_name VARCHAR(50), phone VARCHAR(20), FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE );
2. 一对多关系(One-to-Many)
特点:一个表中的一条记录可以对应另一个表中的多条记录,但反向只能对应一条。
适用场景:最常见的关系(如 “部门 - 员工”“订单 - 订单项”)。
实现方式:在 “多” 的一方添加外键,关联 “一” 的一方的主键。
-- 示例:部门表(一的一方) CREATE TABLE departments ( id INT PRIMARY KEY AUTO_INCREMENT, dept_name VARCHAR(50) NOT NULL ); -- 员工表(多的一方,关联部门表) CREATE TABLE employees ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, dept_id INT, -- 外键关联部门表 FOREIGN KEY (dept_id) REFERENCES departments(id) ON DELETE SET NULL );
3. 多对多关系(Many-to-Many)
特点:两个表中的记录可以互相对应多条记录(如 “学生 - 课程”:一个学生可选多门课,一门课可被多个学生选)。
适用场景:需要双向一对多的业务关系。
实现方式:通过中间表关联两个表,中间表至少包含两个外键,分别指向两个表的主键。
-- 示例:学生表 CREATE TABLE students ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL ); -- 课程表 CREATE TABLE courses ( id INT PRIMARY KEY AUTO_INCREMENT, course_name VARCHAR(100) NOT NULL ); -- 中间表(关联学生和课程,实现多对多) CREATE TABLE student_courses ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, -- 联合唯一约束,避免重复关联 UNIQUE KEY (student_id, course_id), FOREIGN KEY (student_id) REFERENCES students(id) ON DELETE CASCADE, FOREIGN KEY (course_id) REFERENCES courses(id) ON DELETE CASCADE );
关系设计的注意事项
外键约束
强制关联关系,可选参数:
ON DELETE CASCADE
:主表记录删除时,从表关联记录也删除ON DELETE SET NULL
:主表记录删除时,从表外键设为NULL
(需外键允许NULL
)
避免循环依赖:多表关联时避免形成闭环(如 A 依赖 B,B 依赖 C,C 依赖 A),可能导致数据操作异常。
中间表设计:多对多的中间表可额外添加字段(如 “学生选课表” 可加
score
字段记录成绩)。
1. 交叉连接(CROSS JOIN)
返回两个表的笛卡尔积(所有可能的组合),通常需要配合 WHERE
条件过滤无意义的记录。
-- 语法 SELECT 列名 FROM 表1 CROSS JOIN 表2 [WHERE 条件]; -- 示例:查询学生和他们的课程(未过滤前是所有学生与所有课程的组合) SELECT students.name, courses.course_name FROM students CROSS JOIN courses WHERE students.id = courses.student_id; -- 过滤出有效的关联记录
2. 内连接(INNER JOIN)
只返回两个表中满足连接条件的记录(即匹配的记录),是最常用的连接方式。
-- 语法 SELECT 列名 FROM 表1 INNER JOIN 表2 ON 连接条件; -- 示例:查询学生及其选修的课程(只显示有选课记录的学生) SELECT students.name, courses.course_name FROM students INNER JOIN courses ON students.id = courses.student_id;
3. 外连接(OUTER JOIN)
返回一个表的所有记录,以及另一个表中满足条件的匹配记录。外连接分为:
左外连接(LEFT JOIN / LEFT OUTER JOIN):返回左表所有记录 + 右表匹配记录
右外连接(RIGHT JOIN / RIGHT OUTER JOIN):返回右表所有记录 + 左表匹配记录
全外连接(FULL JOIN):返回两个表的所有记录(MySQL 不直接支持,需用
UNION
模拟)
-- 左外连接示例:查询所有学生及其选修的课程(包括未选课的学生) SELECT students.name, courses.course_name FROM students LEFT JOIN courses ON students.id = courses.student_id; -- 右外连接示例:查询所有课程及选修该课程的学生(包括无学生选修的课程) SELECT students.name, courses.course_name FROM students RIGHT JOIN courses ON students.id = courses.student_id;
4. 自连接(SELF JOIN)
将表与自身进行连接,通常用于查询表中具有层级或关联关系的数据(如员工与上司)。
-- 示例:查询员工及其直属上司的姓名(假设 employees 表中有 manager_id 关联上司ID) SELECT e.name AS 员工, m.name AS 上司 FROM employees e LEFT JOIN employees m ON e.manager_id = m.id;
5. 联合查询(UNION / UNION ALL)
用于合并多个 SELECT
语句的结果集,要求列数和数据类型一致。
UNION
:去除重复记录UNION ALL
:保留所有记录(包括重复)
-- 示例:合并两个查询结果(查询10岁以下和20岁以上的学生) SELECT name, age FROM students WHERE age < 10 UNION SELECT name, age FROM students WHERE age > 20;
子查询(Subquery)是嵌套在另一个 SQL 语句中的查询,也称为内部查询,而包含子查询的语句称为外部查询。子查询通常用于为主查询提供数据或条件,可使复杂查询逻辑更清晰。
子查询的基本特点
子查询必须用括号
()
包裹子查询通常放在
WHERE
、FROM
或SELECT
子句中子查询的结果可以是单个值、一行、一列或多行
常见子查询类型及示例
1. 返回单个值的子查询(标量子查询)
常用于 WHERE
子句中,配合比较运算符(=
, >
, <
, >=
, <=
等)使用。
-- 示例:查询与"张三"同龄的学生 SELECT name, age FROM students WHERE age = (SELECT age FROM students WHERE name = '张三');
2. 返回一列值的子查询(列子查询)
通常与 IN
、NOT IN
、ANY
、ALL
等关键字配合使用
-- 示例1:查询选修了"数学"课程的学生(使用IN) SELECT name FROM students WHERE id IN ( SELECT student_id FROM courses WHERE course_name = '数学' ); -- 示例2:查询年龄大于所有女生的男生(使用ALL) SELECT name, age FROM students WHERE gender = '男' AND age > ALL (SELECT age FROM students WHERE gender = '女');
3. 返回多行多列的子查询(表子查询)
可作为一个临时表,用于 FROM
子句中(通常需要起别名)。
-- 示例:查询每个班级的平均年龄及班级信息 SELECT classes.class_name, avg_ages.avg_age FROM classes JOIN ( SELECT class_id, AVG(age) AS avg_age FROM students GROUP BY class_id ) AS avg_ages ON classes.id = avg_ages.class_id;
4. EXISTS 子查询
用于判断子查询是否返回结果,返回 TRUE
或 FALSE
,不关心具体数据。
-- 示例:查询有选修课程的学生(存在选课记录) SELECT name FROM students s WHERE EXISTS ( SELECT 1 FROM courses c WHERE c.student_id = s.id );
5. 嵌套子查询
子查询中可以再包含子查询,形成多层嵌套(但建议层数不宜过多,以免影响性能)。
-- 示例:查询选修了"张三"所选全部课程的学生 SELECT DISTINCT s.name FROM students s JOIN courses c ON s.id = c.student_id WHERE c.course_name IN ( SELECT course_name FROM courses WHERE student_id = (SELECT id FROM students WHERE name = '张三') ) GROUP BY s.name HAVING COUNT(c.course_name) = ( SELECT COUNT(*) FROM courses WHERE student_id = (SELECT id FROM students WHERE name = '张三') );
子查询的注意事项
性能问题:复杂子查询可能影响效率,可尝试用
JOIN
改写(部分场景下JOIN
性能更优)。关联子查询与非关联子查询
:
非关联子查询:独立于外部查询,只执行一次
关联子查询:依赖外部查询的字段,可能执行多次(每行一次)
SELECT
子句中的子查询:只能返回单个值,用于为结果集增加计算列。
多表查询的注意事项
表别名:使用
AS
为表起别名(可省略AS
),简化 SQL 语句。列名冲突:当多表中有同名列时,需用
表名.列名
或别名.列名
区分(如students.id
)。性能优化:对连接条件中的列建立索引,避免不必要的
SELECT *
,减少数据扫描量。
四. 事务
在 MySQL 中,事务(Transaction)是一组数据库操作的集合,这些操作要么全部成功执行,要么全部失败回滚,以此保证数据的一致性和完整性。事务是数据库管理系统(DBMS)处理并发操作和故障恢复的核心机制。
MySQL 事务的 ACID 特性
事务必须满足以下四个基本特性,即 ACID 特性:
原子性(Atomicity) 事务中的所有操作要么全部执行成功,要么全部失败回滚(Rollback),不会出现部分执行的情况。 例如:转账操作中,“A 账户扣款” 和 “B 账户到账” 必须同时成功或同时失败。
一致性(Consistency) 事务执行前后,数据库从一个一致的状态转变为另一个一致的状态,数据规则(如约束、逻辑关系)不会被破坏。 例如:转账前后,A 和 B 的账户总金额保持不变。
隔离性(Isolation) 多个事务并发执行时,一个事务的操作不会被其他事务干扰,各事务之间相互隔离。 MySQL 通过隔离级别控制并发事务的交互程度(见下文)。
持久性(Durability) 事务一旦提交(Commit),其对数据的修改会永久保存到数据库中,即使系统崩溃也不会丢失。
MySQL 事务的操作语法
MySQL 中,事务的基本操作通过以下 SQL 命令实现:
开启事务 关闭自动提交模式,后续操作将纳入事务管理:
START TRANSACTION; -- 或 BEGIN;
执行操作 执行一系列 SQL 语句(如 INSERT、UPDATE、DELETE 等):
UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- A账户扣款 UPDATE accounts SET balance = balance + 100 WHERE id = 2; -- B账户到账
提交事务 确认所有操作成功,将修改永久保存到数据库:
COMMIT;
回滚事务 若操作失败,撤销所有已执行的修改,恢复到事务开始前的状态:
ROLLBACK;
设置保存点(可选) 在事务中设置中间点,可回滚到指定保存点(而非整个事务):
SAVEPOINT sp1; -- 创建保存点 sp1 ROLLBACK TO sp1; -- 回滚到 sp1
MySQL 事务的隔离级别
多个事务并发执行时,可能出现 脏读、不可重复读、幻读 等问题。MySQL 定义了四种隔离级别(从低到高),用于平衡隔离性和并发性能:
读未提交(READ UNCOMMITTED) 最低隔离级别,一个事务可读取另一个未提交的修改。可能导致 脏读(读取到未提交的无效数据)。
读已提交(READ COMMITTED) 一个事务只能读取另一个已提交的修改,避免 脏读,但可能出现 不可重复读(同一事务中多次读取同一数据,结果不一致)。 这是大多数数据库的默认级别(如 Oracle)。
可重复读(REPEATABLE READ) MySQL 的默认隔离级别。保证同一事务中多次读取同一数据的结果一致,避免 脏读 和 不可重复读,但可能出现 幻读(事务执行中,其他事务新增的数据被读取到)。
串行化(SERIALIZABLE) 最高隔离级别,强制事务串行执行(类似单线程),避免所有并发问题,但性能最差。
查看和设置隔离级别
查看当前隔离级别:
-- 查看全局隔离级别 SELECT @@GLOBAL.transaction_isolation; -- 查看当前会话隔离级别 SELECT @@SESSION.transaction_isolation;
设置隔离级别(需有足够权限):
-- 设置全局隔离级别(对新会话生效) SET GLOBAL transaction_isolation = 'READ COMMITTED'; -- 设置当前会话隔离级别 SET SESSION transaction_isolation = 'REPEATABLE READ';
注意事项
存储引擎支持:MySQL 中,只有 InnoDB 存储引擎支持事务,MyISAM 等引擎不支持。
自动提交:MySQL 默认开启
AUTOCOMMIT=1
(每条 SQL 自动作为一个事务提交),开启事务后需手动提交或回滚。锁机制:事务的隔离性通过 InnoDB 的锁机制(如行锁、表锁)和 MVCC(多版本并发控制)实现。