MySQL基础:数据库与数据表的概念与操作
概括总结对MySQL数据库与数据表的增删改查操作与相关概念
## 数据库的增删改查操作 ```sql -- 显示所有已存在的数据库(用于查看当前数据库环境中的数据库列表) SHOW DATABASES; -- 获取当前正在使用的数据库名称(用于验证当前会话的数据库上下文) SELECT DATABASE(); -- 切换到名为demo的数据库 USE demo; -- 创建名为demo的数据库(如果已存在则不执行任何操作) CREATE DATABASE IF NOT EXISTS demo [CHARACTER SET utf8mb4] -- 可选字符集声明(默认为utf8mb4) [COLLATE utf8mb4_general_ci];-- 可选排序规则声明(默认为utf8mb4_general_ci) -- 删除名为demo的数据库(如果不存在则报错) DROP DATABASE demo; -- 安全删除数据库(如果不存在则忽略错误) DROP DATABASE IF EXISTS demo; -- 修改demo数据库的字符集和排序规则配置 ALTER DATABASE demo CHARACTER SET utf8mb4 -- 使用utf8mb4字符集(支持更多Unicode字符,包括Emoji) COLLATE utf8mb4_0900_ai_ci; -- 使用最新的排序规则(ai=True表示大小写不敏感) ``` --- ## SQL数据类型 ### 常见的SQL数据类型 - 数字类型:整型、浮点型、定点型等。 - 字符串类型:字符型、文本型、枚举型、集合型等。 - 日期时间类型:日期型、日期时间型、时间戳型等。 --- ### 数据类型的属性解释 - `NULL`:数据列可包含NULL值。 - `NOT NULL`:数据列不允许包含NULL值。 - `DEFAULT`:默认值。 - `PRIMARY KEY`:KEY 主键。 - `AUTO_INCREMENT`:自动递增,适用于整数类型。 - `UNSIGNED`:是指数值类型只能为正数。 - `CHARACTER SET name`:指定一个字符集。 - `COMMENT`:对表或者字段说明。 --- ### 整数类型 - 专门用来保存整数 - 区分有符号和无符号,默认就是有符号的 - 可以在数据类型后加上 unsigned 表示是无符号的 - 在设置整型时,可以设置将来显示的位宽,不足的补足,超出不管。 - 默认填充的是空格。 - tinyint(2):设置位宽是 2,如果查询的是1,显示的就是' 1'。 | 类型 | 字节 | 有符号取值范围 | 无符号取值范围 | 说明 | 典型应用场景 | | ------------- | ---- | ------------------------------------------------------ | ------------------------------ | -------- | ------------------------ | | **tinyint** | 1 | -128 ~ 127 | 0 ~ 255 | 超小整数 | 状态码、布尔标志 | | **smallint** | 2 | -32,768 ~ 32,767 | 0 ~ 65,535 | 小整数 | 计数器、评分等级 | | **mediumint** | 3 | -3,888,608 ~ 3,888,607 | 0 ~ 16,777,215 | 中等整数 | 分区标识、中等规模计数器 | | **int** | 4 | -2,147,483,648 ~ 2,147,483,647 | 0 ~ 4,294,967,295 | 标准整数 | 主键自增、常规数值存储 | | **bigint** | 8 | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 | 0 ~ 18,446,744,073,709,551,615 | 大整数 | 用户ID、时间戳、金融计算 | --- ### 浮点类型 - 专门保存小数,会丢失精度 - 不要用来保存不希望丢失精度的数据 | 类型 | 字节 | 精度(有效数字位数) | 说明 | | ---------- | ---- | -------------------- | ------------------------------------------------------------ | | **float** | 4 | 6 | 单精度浮点数(±1.17549435×10⁻³⁸ ~ ±3.4028235×10³⁸) | | **double** | 8 | 16 | 双精度浮点数(±2.225073858497757×10⁻³⁰⁸ ~ ±1.7976931348623157×10³⁰⁸) | --- ### 定点类型 - 专门保存小数,不会丢失精度 - 可以用来保存不希望丢失精度的数据 | 类型 | 说明 | | ----------- | ------------------------------------------------------------ | | **DECIMAL** | `DECIMAL(size, d)`● `size`:数字总位数(整数部分 + 小数部分),● `d`:小数部分位数● **约束条件**: - `size ≥ d + 1` - `d ≥ 0` - `size`最大支持38位 | --- ### 字符串类型 | 数据类型 | 说明 | | --------------------- | ------------------------------------------------------------ | | **char(size)** | 字符型,固定长度字符串,可包含字母、数字及特殊字符。size范围:0~255 | | **varchar(size)** | 字符型,可变长度字符串,可包含字母、数字及特殊字符。size范围:0~65535 | | **tinytext** | 文本类型,长度为0~255的字符串文本 | | **text** | 文本类型,长度为0~65535的字符串文本 | | **mediumtext** | 文本类型,长度为0~16,777,215的字符串文本 | | **longtext** | 文本类型,长度为0~4,294,967,295的字符串文本 | | **enum(v1, v2, ...)** | 枚举类型,插入数据必须匹配预定义值列表中的其中一个值,示例:`ENUM('男', '女')` | | **set(v1, v2, ...)** | 集合类型,插入数据可匹配预定义值列表中的一个或多个值,示例:`SET('A', 'B', 'C')` | --- ### 日期时间类型 - 专门用来保存日期和时间 - datetime 和 timestamp 可以自动初始化和更新为当前日期时间 | 类型 | 格式 | 取值范围 | 说明 | | ------------- | ------------------------- | --------------------------------------------------- | ------------------------------------------------------ | | **year** | `YYYY` | `1901~2155` 或 `0000` | 年份类型,支持四位数字(MySQL 5.7+ 默认范围1901-2155) | | **date** | `YYYY-MM-DD` | `1000-01-01 ~ 9999-12-31` | 日期类型,包含年月日(范围比MySQL默认的1900-0000更广) | | **time** | `hh:mm:ss` 或 `hhh:mm:ss` | `-838:59:59 ~ 838:59:59` | 时间类型,支持负数时间表示凌晨(三位小时格式更易读) | | **datetime** | `YYYY-MM-DD HH:mm:ss` | `1000-01-01 00:00:00 ~ 9999-12-31 23:59:59` | 日期时间组合类型,精度到秒 | | **timestamp** | `YYYY-MM-DD HH:mm:ss` | `1970-01-01 00:00:01 UTC ~ 2038-01-19 03:14:07 UTC` | 时间戳类型,基于Unix时间戳(注意2038年溢出问题) | --- ## 数据表的增删改查操作 ```sql -- 获取当前连接的数据库名称(用于确认当前操作的数据库环境) SELECT DATABASE(); -- 切换到名为 `demo` 的数据库(若数据库不存在会报错) USE demo; -- 查看当前数据库中的所有表(需要 SELECT 权限) SHOW TABLES; /* * 创建表 * 注意: * 1. 最后一列后不能有逗号 * 2. 推荐添加主键约束和默认值 */ CREATE TABLE student ( id INT UNSIGNED, -- 无符号整数类型(0-4294967295) name VARCHAR(20), -- 变长字符串(最多20字符),建议添加 NOT NULL 约束 age TINYINT UNSIGNED, -- 无符号小整数(0-255),适合年龄字段 gender ENUM('男', '女', '保密'), -- 枚举类型(仅允许指定值) createdAt TIMESTAMP -- 时间戳类型(记录创建时间) ); /* * 创建表(如果不存在则跳过,避免重复创建报错) * 适用于需要初始化脚本的场景 */ CREATE TABLE IF NOT EXISTS student ( id INT UNSIGNED, name VARCHAR(20), age TINYINT UNSIGNED, gender ENUM('男', '女', '保密'), createdAt TIMESTAMP ); -- 查看数据表的创建语句,包括表的结构和属性 SHOW CREATE TABLE student; -- 查看表结构 DESCRIBE student; -- 删除表(执行后不可逆!) DROP TABLE student; -- 安全删除表(如果不存在也不报错) DROP TABLE IF EXISTS student; -- 修改表名(新表名不能已存在) ALTER TABLE student RENAME TO stu; /* * 添加字段 */ ALTER TABLE student ADD updatedAt TIMESTAMP; -- 删除字段(需确保字段存在) ALTER TABLE student DROP updatedAt; -- 修改字段类型 ALTER TABLE student MODIFY createdAt DATETIME; -- 同时修改字段名和类型 ALTER TABLE student CHANGE createdAt createAt TIMESTAMP; ``` --- ## SQL约束 ### 1. 主键约束 (PRIMARY KEY) - 作用:唯一标识表中每一行,确保数据完整性。 - 特点: - 必须唯一且非空 (`NOT NULL + UNIQUE`)。 - 一个表只能有一个主键,但可包含多列(复合主键)。 - 示例: ```sql CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) UNIQUE NOT NULL ); ``` --- ### 2. 外键约束 (FOREIGN KEY) - 作用:建立表间关系,强制引用完整性。 - 特点: - 仅InnoDB引擎支持。 - 外键列值必须匹配被引用表的主键/唯一索引。 - 可定义级联操作(如 `ON DELETE CASCADE`)。 - 示例: ```sql CREATE TABLE orders ( order_id INT PRIMARY KEY, user_id INT, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); ``` --- ### 3. 唯一性约束 (UNIQUE) - 作用:确保列或列组合的值唯一。 - 特点: - 允许NULL值(除非同时加 `NOT NULL`)。 - 多个唯一约束可共存。 - 示例: ```sql CREATE TABLE emails ( email VARCHAR(100) UNIQUE, phone VARCHAR(20) UNIQUE ); ``` --- ### 4. 非空约束 (NOT NULL) - 作用:强制列必须填写值,禁止NULL。 - 特点: - 常与 `DEFAULT` 结合使用(如 `NOT NULL DEFAULT 'value'`)。 - 唯一约束允许NULL时,`NOT NULL` 会覆盖此行为。 - 示例: ```sql CREATE TABLE products ( name VARCHAR(50) NOT NULL, price DECIMAL(10,2) NOT NULL DEFAULT 0.00 ); ``` --- ### 5. 默认值约束 (DEFAULT) - 作用:当未指定列值时自动填充默认值。 - 特点: - 数据类型需与列匹配。 - 适用于`INSERT`操作,不影响`UPDATE`(除非显式设置)。 - 示例: ```sql CREATE TABLE logs ( created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` --- ### 6. 检查约束 (CHECK) - 作用:限制列的取值范围或格式。 - 特点: - MySQL语法支持,但实际由存储引擎处理(如InnoDB部分支持)。 - 跨字段逻辑可能失效,需结合触发器或应用层校验。 - 示例: ```sql CREATE TABLE students ( age INT CHECK (age BETWEEN 18 AND 100) ); ``` --- ### 7. 自增约束 (AUTO_INCREMENT) - 作用:为主键列自动生成递增整数。 - 特点: - 仅适用于整数类型列。 - 默认从1开始,可自定义起始值。 - 示例: ```sql CREATE TABLE posts ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(200) ); ``` --- ### 约束对比 | 约束类型 | 唯一性 | 非空 | 允许NULL | 复合支持 | 引擎依赖 | | -------------- | ------ | ---- | -------- | -------- | -------- | | PRIMARY KEY | ✔️ | ✔️ | ❌ | ✔️ | 所有 | | FOREIGN KEY | - | - | - | ❌ | InnoDB | | UNIQUE | ✔️ | ❌ | ✔️ | ✔️ | 所有 | | NOT NULL | - | ✔️ | ❌ | ❌ | 所有 | | DEFAULT | - | - | ✔️ | ❌ | 所有 | | CHECK | - | - | ✔️ | ✔️ | 部分引擎 | | AUTO_INCREMENT | - | ❌ | ❌ | ❌ | 所有 | --- ## 外键约束 ### 什么是外键? **外键(Foreign Key)** 是一个字段(或一组字段),它的值必须匹配另一张表中的 **主键(Primary Key)** 的值。 👉 目的:确保两张表之间的数据关联关系正确,防止「无效引用」。 - 外键通常定义在从表(子表)中,用于引用主表(父表)的主键或唯一键。 - 在一对多关系中,外键始终在“多”的一方(从表)创建。 --- ### 基础外键约束 ```sql CREATE TABLE orders ( order_id INT PRIMARY KEY, user_id INT, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE ); ``` --- ### 复合外键约束 ```sql CREATE TABLE order_details ( order_id INT, product_id INT, quantity INT, PRIMARY KEY (order_id, product_id), FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE, FOREIGN KEY (product_id) REFERENCES products(product_id) ON DELETE RESTRICT ); ``` --- ### 外键的常见规则 1. 匹配主键 外键字段的值必须完全匹配主表主键的值(包括类型和长度)。 2. 级联操作(可选) 可以通过 `ON DELETE` 和 `ON UPDATE` 控制关联行为。 3. 外键操作类型 - 严格操作:`restrict` - 主表中不存在对应的数据,从表不允许添加 - 从表引用着数据,主表对应的数据不允许删除 - 从表引用着数据,主表对应的主键不允许修改 - 置空操作:`set null` - 修改或删除主表 id 时,所有跟它关联的从表字段都会被设置为null。 - 级联操作:`cascade` - 修改或删除主表 id 时,所有跟它关联的从表字段都会做同样的操作。 --- ### 何时用外键? - 当两张表有明确的 **一对多关系** 时(如班级和学生)。 - 需要严格确保数据关联时(如订单和商品)。 - 不推荐过度使用:外键会增加写操作的开销,需权衡灵活性与一致性。

MySQL基础:数据操作查询的方式
概括总结对MySQL中数据的增删改查与条件、排序、分页、聚合、分组、多表的查询操作
## 表中数据的增删改查操作 ### 1. 增(Create) - 关键字:`INSERT INTO ... VALUES ...` - `INSERT INTO`:指定插入的目标表 - `VALUES`:定义要插入的具体值 - 示例: ```sql INSERT INTO users (name, age) VALUES ('张三', 25); ``` --- ### 2. 删(Delete) - 关键字:`DELETE FROM ... WHERE ...` - `DELETE FROM`:指定要删除数据的表 - `WHERE`:条件筛选(⚠️ **不加条件会清空全表!**) - 示例: ```sql DELETE FROM users WHERE id = 100; ``` --- ### 3. 改(Update) - 关键字:`UPDATE ... SET ... WHERE ...` - `UPDATE`:指定要修改的表 - `SET`:设置新值 - `WHERE`:条件筛选(⚠️ **不加条件会更新全表!**) - 示例: ```sql UPDATE users SET age = 26 WHERE name = '张三'; ``` --- ### 4. 查(Read) - 关键字:`SELECT ... FROM ... WHERE ...` - `SELECT`:指定要查询的列(`*` 表示所有列) - `FROM`:指定查询的表 - `WHERE`:条件筛选 - `ORDER BY`:排序(`ASC` 升序,`DESC` 降序) - `LIMIT`:限制返回的行数 - 示例: ```sql SELECT name, age FROM users WHERE age > 20 ORDER BY id DESC LIMIT 10; ``` --- ### 清空表(高危操作!) ```sql TRUNCATE TABLE users; -- 不可回滚,谨慎使用! ``` --- ## 基础查询和Where条件查询 ### 一、基本查询操作(SELECT) 语法: ```sql SELECT 列1, 列2, ... FROM 表名 [WHERE 条件] [其他子句...]; ``` #### 常见用法 1. 查询所有列: ```sql SELECT * FROM employees; ``` 2. 查询指定列: ```sql SELECT name, salary FROM employees; ``` 3. 去重查询(DISTINCT): ```sql SELECT DISTINCT department FROM employees; ``` 4. 计算列(表达式或函数): ```sql SELECT name, salary * 1.1 AS new_salary FROM employees; ``` --- ### 二、WHERE 子句 `WHERE` 子句用于筛选满足条件的记录,支持多种运算符和逻辑组合。 #### 1. 比较运算符 - `=`:等于 ```sql SELECT * FROM employees WHERE salary = 5000; ``` - `<>` 或 `!=`:不等于 - `>`、`<`、`>=`、`<=`:数值或日期比较 ```sql SELECT * FROM employees WHERE hire_date > '2020-01-01'; ``` - `BETWEEN ... AND ...`:范围查询 ```sql SELECT * FROM employees WHERE salary BETWEEN 4000 AND 6000; ``` #### 2. 逻辑运算符 - `AND`:同时满足多个条件 ```sql SELECT * FROM employees WHERE department = 'IT' AND salary > 5000; ``` - `OR`:满足任意一个条件 ```sql SELECT * FROM employees WHERE department = 'HR' OR department = 'Sales'; ``` - `NOT`:取反条件 ```sql SELECT * FROM employees WHERE NOT department = 'IT'; ``` #### 3. 模糊查询(LIKE) - `%`:匹配任意字符(包括空字符) ```sql SELECT * FROM employees WHERE name LIKE 'J%'; -- 以 J 开头(如 John, Jane) SELECT * FROM products WHERE name LIKE '%apple%'; -- 包含 "apple"(如 Pineapple, ApplePie) ``` - `_`:匹配单个字符 ```sql SELECT * FROM employees WHERE name LIKE '_a%'; -- 第二个字符是 a(如 Sam, David) SELECT * FROM users WHERE phone LIKE '138-____-____'; -- 固定格式(如 138-1234-5678) ``` - 转义特殊字符(如 `%` 或 `_`): ```sql SELECT * FROM discounts WHERE remark LIKE '%10!%%' ESCAPE '!'; -- 匹配包含 "10%" 的文本 ``` - 不区分大小写: ```sql SELECT * FROM users WHERE username LIKE '%admin%' COLLATE utf8_general_ci; -- 匹配 Admin, ADMIN ``` #### 4. 集合查询(IN) 筛选字段值在指定集合中的记录: ```sql SELECT * FROM employees WHERE department IN ('IT', 'HR'); ``` #### 5. 处理 NULL 值 - `IS NULL`:判断为空 ```sql SELECT * FROM employees WHERE manager_id IS NULL; ``` - `IS NOT NULL`:判断非空 ```sql SELECT * FROM employees WHERE email IS NOT NULL; ``` --- ### 三、WHERE 子句的注意事项 1. 执行顺序:`WHERE` 在数据筛选阶段生效,早于 `GROUP BY`、`ORDER BY` 等子句。 2. 性能优化: • 对索引列使用 `WHERE` 条件可提高效率。 • 避免在条件中对列做函数操作(如 `WHERE YEAR(date) = 2023`)。 3. 模糊查询性能: • `LIKE '%keyword%'` 会导致全表扫描,建议对高频搜索字段使用全文索引。 • 前导通配符(如 `LIKE '%abc'`)无法利用索引。 --- ## 排序和分页查询 ### 一、排序(ORDER BY) 1. 基本语法 ```sql SELECT 列名 FROM 表名 ORDER BY 列1 [ASC|DESC], 列2 [ASC|DESC], ...; ``` • `ASC`:升序(默认),`DESC`:降序。 • 支持多列排序,优先级按列顺序依次降低。 2. 示例 ```sql SELECT name, salary FROM employees ORDER BY salary DESC, hire_date ASC; -- 先按工资降序,再按入职时间升序 ``` 3. 注意事项 - 索引优化:排序字段若没有索引,大数据量时可能导致性能问题(需磁盘文件排序)。 - 稳定性:若排序字段有重复值,结果顺序可能不稳定,可添加唯一字段(如主键)作为次要排序条件。 --- ### 二、分页查询(LIMIT & OFFSET) 1. 基本语法 ```sql SELECT 列名 FROM 表名 LIMIT 每页条数 OFFSET 跳过行数; ``` - 简写形式:`LIMIT 跳过行数, 每页条数`(如`LIMIT 20, 10`表示跳过20行,取10行)。 2. 示例 ```sql SELECT * FROM products ORDER BY price DESC LIMIT 10 OFFSET 20; -- 获取第3页数据(每页10条) ``` 3. 性能优化 - 深分页问题:`OFFSET`值过大时(如`LIMIT 100000, 10`),MySQL需扫描大量数据后跳过,效率低下。 - 优化方案: - 基于游标的分页:记录上一页最后一条数据的唯一标识(如自增ID),下页查询时使用`WHERE id > last_id LIMIT 10`。 - 子查询优化: ```sql SELECT * FROM table WHERE id >= (SELECT id FROM table ORDER BY id LIMIT 100000, 1) ORDER BY id LIMIT 10; ``` --- ### 三、排序 + 分页的常见场景 1. 典型查询 ```sql SELECT id, title, created_at FROM articles ORDER BY created_at DESC LIMIT 10 OFFSET 0; -- 首页按时间倒序排列 ``` 2. 需注意的问题 - 结果一致性:若数据在分页间变化(如新增/删除),可能导致重复或遗漏。 - 覆盖索引:为排序和过滤字段创建联合索引(如`(created_at, id)`),避免回表查询。 --- ### 四、高级用法(MySQL 8.0+) 1. 窗口函数分页 使用`ROW_NUMBER()`实现灵活分页(适用于复杂排序逻辑): ```sql SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num FROM employees ) AS tmp WHERE row_num BETWEEN 21 AND 30; ``` --- ### 总结 - 排序:用`ORDER BY`控制结果顺序,注意索引优化。 - 分页:用`LIMIT`和`OFFSET`,但深分页需优化。 - 组合使用:通常先排序再分页,确保结果顺序稳定。 - 性能关键:尽量通过索引减少全表扫描和文件排序,避免`OFFSET`过大时的性能瓶颈。 --- ## 聚合函数 ### 一、常用聚合函数 | 函数 | 作用 | 示例(假设表 `sales`) | | -------------------- | ---------------------- | ------------------------------------------ | | **`COUNT()`** | 统计行数(可过滤NULL) | `SELECT COUNT(*) FROM sales;` | | **`SUM()`** | 数值列求和 | `SELECT SUM(amount) FROM sales;` | | **`AVG()`** | 数值列平均值 | `SELECT AVG(price) FROM sales;` | | **`MAX()`** | 返回列最大值 | `SELECT MAX(sale_date) FROM sales;` | | **`MIN()`** | 返回列最小值 | `SELECT MIN(quantity) FROM sales;` | | **`GROUP_CONCAT()`** | 合并分组字符串 | `SELECT GROUP_CONCAT(product) FROM sales;` | | **`STD()`** | 标准差 | `SELECT STD(revenue) FROM sales;` | --- ### 二、核心使用场景 1. 与 `GROUP BY` 结合 ```sql SELECT product_type, COUNT(*) AS total_sales, AVG(price) -- 使用 AS 关键字 给统计列字段定义别名 FROM sales GROUP BY product_type; -- 按商品类型分组统计 ``` 2. 过滤聚合结果(`HAVING`) ```sql SELECT user_id, SUM(amount) AS total_spent FROM orders GROUP BY user_id HAVING total_spent > 1000; -- 筛选消费超过1000的用户 ``` 3. 去重统计(`DISTINCT`) ```sql SELECT COUNT(DISTINCT user_id) FROM orders; -- 统计唯一用户数 ``` --- ### 三、注意事项 1. `NULL` 值的处理 - 除 `COUNT(*)` 外,其他聚合函数默认忽略 `NULL` 值。 - 示例:`AVG(score)` 仅计算非 NULL 值的平均数。 2. `GROUP BY` 的隐式排序 - MySQL 8.0 前,`GROUP BY` 默认按分组字段排序;8.0 后取消该特性。若需排序,显式使用 `ORDER BY`。 3. `WHERE` 与 `HAVING` 的区别 - `WHERE`:在分组前过滤行(不能使用聚合函数)。 - `HAVING`:在分组后过滤聚合结果(可使用聚合函数)。 4. 聚合函数嵌套限制 - 聚合函数不能直接嵌套(如 `SUM(AVG(price))`),需通过子查询实现。 --- ### 四、性能优化 1. 索引对聚合的影响 - `MIN()`/`MAX()` 在索引列上效率极高,几乎无需扫描数据。 - `COUNT(列)` 比 `COUNT(*)` 慢(需检查 NULL)。 2. 减少全表扫描 - 尽量通过 `WHERE` 缩小聚合范围: ```sql SELECT AVG(score) FROM users WHERE age > 18; ``` 3. 避免 `GROUP BY` 的临时表 - 使用 `EXPLAIN` 检查执行计划,确保分组字段有索引。 --- ### 五、扩展用法 1. 组合聚合函数 ```sql SELECT COUNT(*) AS total_orders, SUM(amount) AS total_revenue, AVG(amount) AS avg_order_value FROM orders; ``` 2. 与 `WITH ROLLUP` 生成小计 ```sql SELECT product_type, SUM(quantity) FROM sales GROUP BY product_type WITH ROLLUP; -- 添加汇总行 ``` 3. 窗口函数中的聚合(MySQL 8.0+) ```sql SELECT order_id, amount, SUM(amount) OVER (PARTITION BY user_id) AS user_total FROM orders; ``` --- ### 总结 - 核心用途:统计、汇总、分组分析数据。 - 性能关键:合理使用索引,减少全表扫描。 - 易错点:`NULL` 值处理、`HAVING` 与 `WHERE` 混淆、隐式排序依赖。 --- ## 分组和HAVING条件查询 ### 一、分组(GROUP BY) #### 1. 作用 将数据按指定列的值分组,对每组进行聚合统计(如计数、求和、平均值等)。 #### 2. 基本语法 ```sql SELECT 分组列, 聚合函数(列) FROM 表名 GROUP BY 分组列1, 分组列2...; ``` #### 3. 示例 统计每个部门的员工数量和平均工资: ```sql SELECT department_id, COUNT(*) AS employee_count, -- 配合聚合函数使用 AVG(salary) AS avg_salary FROM employees GROUP BY department_id; ``` #### 4. 关键特性 - 多列分组:可指定多个分组列(如 `GROUP BY department_id, job_title`)。 - 隐式去重:分组后每组仅保留唯一值组合。 - NULL 值处理:所有 `NULL` 值会被归为同一组。 --- ### 二、HAVING 条件查询 #### 1. 作用 过滤分组后的结果(类似 `WHERE`,但作用于分组后的数据)。 #### 2. 基本语法 ```sql SELECT 分组列, 聚合函数(列) FROM 表名 GROUP BY 分组列 HAVING 条件; -- 条件可包含聚合函数 ``` #### 3. 示例 筛选出员工数量超过 5 人且平均工资高于 8000 的部门: ```sql SELECT department_id, COUNT(*) AS employee_count, AVG(salary) AS avg_salary FROM employees GROUP BY department_id HAVING employee_count > 5 AND avg_salary > 8000; ``` --- ### 三、WHERE 与 HAVING 的核心区别 | 特性 | **WHERE** | **HAVING** | | -------------------- | -------------------- | -------------------------- | | **执行阶段** | 分组前过滤行 | 分组后过滤组 | | **是否可用聚合函数** | 否(直接操作原始列) | 是(操作分组后的聚合结果) | | **性能影响** | 减少分组处理的数据量 | 仅过滤最终结果 | --- ### 四、使用场景与技巧 #### 1. 典型场景 - 统计业务指标(如用户订单数、商品销售额)。 - 数据分层分析(如按年龄段、地区分组统计)。 #### 2. 优化技巧 - 索引优化:为分组字段创建索引(如 `department_id`)可加速分组操作。 - 减少分组列:分组列越多,计算开销越大。 - 先 WHERE 再 HAVING:先用 `WHERE` 过滤无关数据,减少分组计算量。 #### 3. 结合 ORDER BY 分组后排序结果: ```sql SELECT department_id, AVG(salary) AS avg_salary FROM employees GROUP BY department_id HAVING avg_salary > 5000 ORDER BY avg_salary DESC; ``` --- ### 五、常见错误与注意事项 1. SELECT 列未分组或聚合 ```sql -- 错误示例:name 未在 GROUP BY 或聚合函数中 SELECT name, AVG(salary) FROM employees GROUP BY department_id; ``` - 解决:仅选择分组列或聚合函数结果。 2. 混淆 WHERE 与 HAVING ```sql -- 错误示例:在 WHERE 中使用聚合函数 SELECT department_id FROM employees WHERE AVG(salary) > 5000; ``` - 解决:聚合条件必须放在 `HAVING` 中。 3. NULL 值的分组统计 - 若分组列包含 `NULL`,所有 `NULL` 值会被归为同一组。 --- ### 六、扩展用法 #### 1. WITH ROLLUP 小计汇总 生成分组小计和总计行: ```sql SELECT department_id, COUNT(*) FROM employees GROUP BY department_id WITH ROLLUP; -- 结果包含每个部门的人数及总人数 ``` #### 2. 多级分组筛选 按多列分组并筛选: ```sql SELECT country, city, COUNT(*) AS user_count FROM users GROUP BY country, city HAVING user_count > 100; ``` --- ### 总结 - GROUP BY:按列分组并进行聚合计算,核心用于数据汇总。 - HAVING:过滤分组后的结果,条件可包含聚合函数。 - 最佳实践:优先用 `WHERE` 减少数据量,合理使用索引优化分组性能。 --- ## 多表查询 ### 一、多表查询类型及语法 #### 1. 内连接(INNER JOIN) - 作用:返回两表中匹配的行(交集)。 - 语法: ```sql SELECT 列名 FROM 表1 INNER JOIN 表2 ON 表1.列 = 表2.列; ``` - 示例: ```sql SELECT orders.order_id, customers.name FROM orders INNER JOIN customers ON orders.customer_id = customers.id; ``` #### 2. 左外连接(LEFT JOIN) - 作用:返回左表全部行 + 右表匹配行(不匹配的右表字段为 `NULL`)。 - 特点: - 左边的表是不看条件的,无论条件是否满足,都会返回左表中的所有数据。 - 只有右边的表会看条件,对于右表,只有满足条件的,才会返回。 - 语法: ```sql SELECT 列名 FROM 表1 LEFT JOIN 表2 ON 表1.列 = 表2.列; ``` - 示例(查询所有员工及其部门,包括未分配部门的员工): ```sql SELECT employees.name, departments.dept_name FROM employees LEFT JOIN departments ON employees.dept_id = departments.id; ``` #### 3. 右外连接(RIGHT JOIN) - 作用:返回右表全部行 + 左表匹配行(不匹配的左表字段为 `NULL`)。 - 特点: - 右边的表是不看条件的,无论条件是否满足,都会返回右表中的所有数据。 - 只有左边的表会看条件,对于左表,只有满足条件的,才会返回。 - 语法:类似 `LEFT JOIN`,方向相反。 #### 4. 全外连接(FULL OUTER JOIN) - 作用:返回两表所有行(不匹配的字段为 `NULL`)。 - 注意:MySQL 不直接支持,需用 `LEFT JOIN + RIGHT JOIN + UNION` 模拟。 #### 5. 交叉连接(CROSS JOIN) - 作用:返回两表的笛卡尔积(所有可能的行组合)。 - 语法: ```sql SELECT 列名 FROM 表1 CROSS JOIN 表2; ``` - 慎用:数据量大时性能极差。 #### 6. UNION - UNION 的作用 将 **多个 SELECT 语句的结果集** 合并为一个结果集,并自动去重(除非使用 `UNION ALL`)。 - 基本语法 ```sql SELECT 列1, 列2 FROM 表1 UNION [ALL] SELECT 列1, 列2 FROM 表2 [UNION [ALL] SELECT 列1, 列2 FROM 表3 ...]; ``` - `UNION`:合并结果并去重。 - `UNION ALL`:合并结果但保留重复行(性能更高)。 - 列要求:所有 `SELECT` 的列数、顺序和数据类型必须一致(列名可以不同)。 --- ### 二、多表查询场景 #### 1. 关联数据查询 如订单表关联客户表,获取订单对应的客户信息。 #### 2. 数据补全 使用 `LEFT JOIN` 确保主表数据完整(如统计所有商品,包括未销售的)。 #### 3. 多条件连接 ```sql SELECT * FROM table1 JOIN table2 ON table1.id = table2.id AND table1.status = 'active'; ``` #### 4. 自连接 同一表内关联(如员工表查员工及其经理): ```sql SELECT e.name, m.name AS manager FROM employees e LEFT JOIN employees m ON e.manager_id = m.id; ``` --- ### 三、多表查询的联合使用 #### 1. 多表连接(3张表以上) ```sql SELECT orders.id, customers.name, products.product_name FROM orders JOIN customers ON orders.customer_id = customers.id JOIN products ON orders.product_id = products.id; ``` #### 2. 结合聚合函数 统计每个客户的订单总金额: ```sql SELECT customers.name, SUM(orders.amount) AS total FROM customers LEFT JOIN orders ON customers.id = orders.customer_id GROUP BY customers.id; ``` #### 3. 子查询作为临时表 ```sql SELECT a.name, b.avg_salary FROM employees a JOIN (SELECT dept_id, AVG(salary) AS avg_salary FROM employees GROUP BY dept_id) b ON a.dept_id = b.dept_id; ``` --- ### 四、性能优化与注意事项 1. 索引优化 - 为连接条件列(如 `customer_id`)和筛选条件列创建索引。 2. 避免笛卡尔积 - 确保 `JOIN` 时明确指定 `ON` 或 `WHERE` 条件。 3. 优先使用 `INNER JOIN` - 比 `OUTER JOIN` 效率更高,尽量减少 `NULL` 值处理。 4. 减少连接表的数量 - 多表连接时,剔除无关表或字段。 5. 分阶段查询 - 对复杂查询拆分为多个步骤,用临时表存储中间结果。 --- ### 五、常见错误 1. 未指定连接条件 ```sql -- 错误!产生笛卡尔积(性能灾难) SELECT * FROM orders, customers; ``` 2. 混淆 `ON` 和 `WHERE` - `ON` 用于定义连接条件,`WHERE` 用于过滤结果。 3. 字段歧义 - 多表存在同名字段时需指定表名前缀: ```sql SELECT orders.id, customers.id -- 明确指定表名 FROM orders JOIN customers ... ``` --- ### 总结 - 核心原则:明确连接类型,写清连接条件,避免笛卡尔积。 - 优化关键:索引、减少连接表数量、优先 `INNER JOIN`。 - 实际应用:结合业务场景选择 `JOIN` 或子查询。 --- ## 子查询 ### 一、子查询的定义 子查询是 **嵌套在其他 SQL 语句(如 `SELECT`、`WHERE`、`FROM` 等)中的查询**,用于辅助主查询完成复杂的数据筛选或计算。 --- ### 二、子查询的分类 #### 1. 按返回结果类型分类 | 类型 | 说明 | 示例(假设表 `employees`) | | -------------- | ------------------------ | ------------------------------------------------------------ | | **标量子查询** | 返回单个值(一行一列) | `SELECT name FROM employees WHERE salary = (SELECT MAX(salary) FROM employees);` | | **列子查询** | 返回一列数据(多行一列) | `SELECT name FROM employees WHERE id IN (SELECT manager_id FROM departments);` | | **行子查询** | 返回一行数据(一行多列) | `SELECT * FROM employees WHERE (salary, age) = (SELECT MAX(salary), MIN(age) FROM employees);` | | **表子查询** | 返回多行多列(临时表) | `SELECT * FROM (SELECT name, salary FROM employees) AS tmp WHERE salary > 5000;` | #### 2. 按执行时机分类 - 非关联子查询:子查询可独立执行,不依赖主查询。 ```sql SELECT name FROM employees WHERE salary > (SELECT AVG(salary) FROM employees); ``` - 关联子查询:子查询依赖主查询的字段值,需与外层查询交互。 ```sql SELECT e1.name FROM employees e1 WHERE salary > (SELECT AVG(salary) FROM employees e2 WHERE e2.dept_id = e1.dept_id); ``` --- ### 三、子查询的使用场景 #### 1. 在 `WHERE` 中过滤数据 ```sql -- 查询工资高于平均工资的员工 SELECT name, salary FROM employees WHERE salary > (SELECT AVG(salary) FROM employees); ``` #### 2. 在 `FROM` 中作为临时表(派生表) ```sql -- 统计每个部门的平均工资 SELECT dept_id, avg_salary FROM (SELECT dept_id, AVG(salary) AS avg_salary FROM employees GROUP BY dept_id) AS dept_stats WHERE avg_salary > 8000; ``` #### 3. 在 `SELECT` 中作为计算字段 ```sql -- 查询员工姓名及其部门平均工资 SELECT name, salary, (SELECT AVG(salary) FROM employees e2 WHERE e2.dept_id = e1.dept_id) AS dept_avg FROM employees e1; ``` #### 4. 与 `IN`、`EXISTS` 配合使用 ```sql -- 查询有订单的客户 SELECT name FROM customers WHERE id IN (SELECT customer_id FROM orders); -- 使用 EXISTS(推荐高效写法) SELECT name FROM customers c WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id); ``` --- ### 四、子查询的注意事项 1. 性能问题 - 关联子查询可能效率较低(需多次执行),尽量优化为 `JOIN` 或非关联子查询。 - 使用 `EXISTS` 替代 `IN` 可提升性能(尤其对大数据集)。 2. 子查询结果限制 - 标量子查询必须返回单值,否则会报错(如 `WHERE salary = (SELECT ...)` 中子查询返回多行)。 3. 避免过度嵌套 - 多层嵌套子查询会降低可读性和维护性,可拆分为临时表或公共表达式(CTE,MySQL 8.0+)。 4. NULL 值处理 - 子查询中包含 `NULL` 时,`IN` 和 `NOT IN` 可能返回意外结果(如 `NOT IN (1, NULL)` 始终为 `FALSE`)。 --- ### 五、子查询 vs 连接查询(JOIN) | **场景** | **子查询** | **JOIN** | | ------------ | ---------------------------- | ---------------------------- | | **结果需求** | 需要中间值(如聚合结果) | 需要直接关联字段的完整行数据 | | **可读性** | 适合简单逻辑(如单条件过滤) | 适合多表复杂关联 | | **性能** | 关联子查询可能较慢 | 通常更高效(尤其有索引时) | --- ### 六、高级用法(MySQL 8.0+) #### 公共表达式(CTE) 使用 `WITH` 子句简化复杂子查询: ```sql WITH dept_avg AS ( SELECT dept_id, AVG(salary) AS avg_salary FROM employees GROUP BY dept_id ) SELECT * FROM dept_avg WHERE avg_salary > 10000; ``` --- ### 总结 - 核心作用:辅助主查询完成复杂逻辑,如过滤、计算中间值。 - 优先选择:非关联子查询 > 关联子查询,能用 `JOIN` 时优先用 `JOIN`。 - 性能关键:避免多层嵌套,优化为 `EXISTS` 或 `JOIN`。 - 适用场景:动态条件过滤、数据分阶段处理、结果集复用。

MySQL基础:多表查询案例
概括总结对MySQL中多表查询的三种情况与规则
### 一、一对一关系 #### 场景 用户表(`users`)与用户详情表(`user_details`),每个用户对应一条详情记录。 #### 表结构与外键 ```sql -- 用户表(主表) CREATE TABLE users ( user_id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL ); -- 用户详情表(从表) CREATE TABLE user_details ( user_id INT PRIMARY KEY, -- 主键 + 外键,确保一对一 email VARCHAR(100), phone VARCHAR(20), FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE ); ``` #### 查询示例 ```sql -- 查询用户及其详情(内连接) SELECT u.username, ud.email, ud.phone FROM users u INNER JOIN user_details ud ON u.user_id = ud.user_id; ``` --- ### 二、一对多关系 #### 场景 部门表(`departments`)与员工表(`employees`),一个部门有多个员工。 #### 表结构与外键 ```sql -- 部门表(主表) CREATE TABLE departments ( dept_id INT PRIMARY KEY AUTO_INCREMENT, dept_name VARCHAR(50) NOT NULL ); -- 员工表(从表) CREATE TABLE employees ( emp_id INT PRIMARY KEY AUTO_INCREMENT, emp_name VARCHAR(50) NOT NULL, dept_id INT, -- 外键指向部门表 FOREIGN KEY (dept_id) REFERENCES departments(dept_id) ON DELETE SET NULL ); ``` #### 查询示例 ```sql -- 查询所有部门及其员工(左连接,包括无员工的部门) SELECT d.dept_name, e.emp_name FROM departments d LEFT JOIN employees e ON d.dept_id = e.dept_id; ``` --- ### 三、多对多关系 #### 场景 学生表(`students`)与课程表(`courses`),一个学生可选修多门课程,一门课程可被多个学生选修。 #### 表结构与外键 ```sql -- 学生表(主表) CREATE TABLE students ( student_id INT PRIMARY KEY AUTO_INCREMENT, student_name VARCHAR(50) NOT NULL ); -- 课程表(主表) CREATE TABLE courses ( course_id INT PRIMARY KEY AUTO_INCREMENT, course_name VARCHAR(50) NOT NULL ); -- 中间表(选课关系表) CREATE TABLE student_courses ( student_id INT, course_id INT, PRIMARY KEY (student_id, course_id), -- 联合主键 FOREIGN KEY (student_id) REFERENCES students(student_id) ON DELETE CASCADE, FOREIGN KEY (course_id) REFERENCES courses(course_id) ON DELETE CASCADE ); ``` #### 查询示例 ```sql -- 查询学生及其选修的课程(三表连接) SELECT s.student_name, c.course_name FROM students s INNER JOIN student_courses sc ON s.student_id = sc.student_id INNER JOIN courses c ON sc.course_id = c.course_id; ``` --- ### 四、外键创建的核心规则 1. 语法 ```sql FOREIGN KEY (当前表列名) REFERENCES 目标表(目标表列名) [ON DELETE {CASCADE | SET NULL | NO ACTION}] [ON UPDATE {CASCADE | NO ACTION}] ``` 2. 注意事项 - 数据类型:外键列与目标列的数据类型必须一致。 - 索引:外键列会自动创建索引(MySQL 隐式处理)。 - 级联操作: - `ON DELETE CASCADE`:主表删除记录时,从表关联记录自动删除。 - `ON DELETE SET NULL`:主表删除记录时,从表外键字段设为 `NULL`。 --- ### 五、总结 | **关系类型** | **外键位置** | **典型场景** | | ------------ | ------------------------ | -------------------------- | | 一对一 | 从表主键同时作为外键 | 主表与扩展表(如用户详情) | | 一对多 | 从表添加外键指向主表主键 | 部门与员工、分类与商品 | | 多对多 | 中间表创建双外键 | 学生选课、用户角色分配 | - 查询逻辑: - 一对一/一对多用 `INNER JOIN` 或 `LEFT JOIN`。 - 多对多通过中间表多次 `INNER JOIN`。 - 外键约束:确保数据一致性,避免脏数据。

MySQL基础:事务
MySQL的事务的概念与操作
### MySQL 事务 #### 1. 事务的概念 事务(Transaction)是数据库操作的最小逻辑单元,由一组 SQL 语句组成。事务具有 **ACID** 特性: - 原子性(Atomicity):事务要么全部执行成功,要么全部失败回滚。 - 一致性(Consistency):事务执行前后,数据库状态保持一致(如约束、触发器等)。 - 隔离性(Isolation):多个事务并发执行时,互不干扰。 - 持久性(Durability):事务提交后,修改永久保存到数据库。 --- #### 2. 事务控制语句 - `START TRANSACTION` 或 `BEGIN`:显式开启事务。 - `COMMIT`:提交事务,永久保存修改。 - `ROLLBACK`:回滚事务,撤销所有未提交的操作。 - `SAVEPOINT`:设置保存点,允许部分回滚。 --- #### 3. 事务示例 ##### 示例 1:转账操作(原子性) ```sql START TRANSACTION; -- 账户 A 扣款 100 UPDATE account SET balance = balance - 100 WHERE user_id = 'A'; -- 账户 B 收款 100 UPDATE account SET balance = balance + 100 WHERE user_id = 'B'; -- 检查是否出错 IF (无错误) THEN COMMIT; -- 提交事务 ELSE ROLLBACK; -- 回滚事务 END IF; ``` ##### 示例 2:订单与库存一致性 ```sql START TRANSACTION; -- 插入订单记录 INSERT INTO orders (order_id, product_id, quantity) VALUES (1001, 1, 2); -- 减少库存 UPDATE products SET stock = stock - 2 WHERE product_id = 1; -- 若库存不足或其他错误 IF (库存 < 0) THEN ROLLBACK; -- 回滚,取消订单和库存修改 ELSE COMMIT; -- 提交事务 END IF; ``` ##### 示例 3:保存点(Partial Rollback) ```sql START TRANSACTION; INSERT INTO logs (message) VALUES ('Step 1'); SAVEPOINT step1; INSERT INTO logs (message) VALUES ('Step 2'); SAVEPOINT step2; -- 回滚到 step1,撤销 Step 2 的操作 ROLLBACK TO step1; COMMIT; -- 最终只有 Step 1 被提交 ``` --- #### 4. 事务隔离级别 MySQL 支持 4 种隔离级别(默认是 `REPEATABLE READ`): 1. READ UNCOMMITTED:可能读到未提交的数据(脏读)。 2. READ COMMITTED:只能读到已提交的数据。 3. REPEATABLE READ(默认):保证同一事务多次读取结果一致。 4. SERIALIZABLE:完全串行化,避免并发问题。 设置隔离级别: ```sql SET TRANSACTION ISOLATION LEVEL READ COMMITTED; ``` --- #### 5. 注意事项 - 隐式提交:部分语句(如 `ALTER TABLE`)会自动提交当前事务。 - 避免长事务:长时间未提交的事务可能导致锁竞争和性能问题。 - 死锁处理:MySQL 会自动检测死锁并回滚其中一个事务。 --- 通过事务,可以确保复杂操作的可靠性和数据一致性,尤其在金融、电商等高并发场景中至关重要。

MySQL:索引
MySQL的索引的概念与操作
### MySQL索引 #### 1. 索引的作用 - 加速查询:通过减少全表扫描,快速定位数据,类似于书籍目录。 - 约束数据:如唯一索引保证列值的唯一性,主键索引标识唯一记录。 #### 2. 索引类型 - 主键索引(PRIMARY KEY):唯一且非空,每个表只能有一个,InnoDB中为聚簇索引(数据存储在B+树叶子节点)。 - 唯一索引(UNIQUE):列值唯一,允许空值,用于避免重复数据。 - 普通索引(INDEX):无唯一性限制,单纯加速查询。 - 全文索引(FULLTEXT):针对文本内容分词搜索,适用于`MATCH AGAINST`操作,InnoDB(5.6+)和MyISAM支持。 - 空间索引(SPATIAL):用于地理数据类型(如GEOMETRY),仅MyISAM支持。 - 复合索引:多列组合的索引,遵循最左前缀原则。 #### 3. **数据结构** - B+树索引(默认): - 支持范围查询、排序、模糊匹配(如`LIKE 'prefix%'`)。 - 叶子节点存储数据或主键(聚簇索引 vs 非聚簇索引)。 - 哈希索引: - 仅支持精确查询(=, IN),速度快但不支持排序/范围查询。 - 显式支持于Memory引擎,InnoDB有自适应哈希索引(内部自动管理)。 - 其他:R树(空间索引)、倒排索引(全文搜索)。 #### 4. 优点与缺点 - 优点: - 大幅提升查询效率,减少磁盘I/O。 - 唯一性约束保证数据完整性。 - 覆盖索引可避免回表,提升性能。 - 缺点: - 占用额外存储空间。 - 增删改操作需维护索引,降低写性能。 - 不合理的索引设计可能拖慢查询。 #### 5. 使用注意事项 - 适用场景: - WHERE、JOIN、ORDER BY、GROUP BY中的高频列。 - 高选择性列(如用户ID)优于低选择性列(如性别)。 - 索引优化: - 最左前缀原则:复合索引`(a,b,c)`需从最左列开始使用,如`a=1 AND b=2`生效,但`b=2`不生效。 - 覆盖索引:查询字段均包含在索引中,避免回表。 - 前缀索引:对长字符串前N个字符建索引,节省空间(如`INDEX(email(10))`)。 - 避免索引失效:如对索引列使用函数、类型转换、`LIKE '%后缀'`、OR连接非索引列等。 - 维护技巧: - 定期分析表(`ANALYZE TABLE`)更新统计信息。 - 使用`EXPLAIN`查看执行计划,优化查询语句。 - 删除冗余或使用率低的索引。 #### 6. 示例与操作 - 创建索引: ```sql CREATE INDEX idx_name ON table(column); ALTER TABLE table ADD UNIQUE (column); ``` - 查看索引: ```sql SHOW INDEX FROM table; ``` - 删除索引: ```sql DROP INDEX idx_name ON table; ``` #### 7. 总结 合理设计索引是数据库性能优化的关键。需权衡查询加速与写操作成本,结合业务场景选择索引类型和列,并通过执行计划分析持续调优。避免过度索引,优先考虑高频、高选择性查询条件,并利用复合索引和覆盖索引提升效率。

MyBatis用法教程
在SpringBoot中集成MyBatis,并且进行增删改查操作
### 一、Spring Boot 集成 MyBatis
#### 1. 添加依赖
```xml














