# Mysql数据库

## 安装启动Mysql

**mysql**

- 是 MySQL 的命令行**客户端**工具，用于连接、查询和管理 MySQL 数据库。
- 你可以通过它来执行 SQL 命令、查看数据和管理数据库。

**mysqld**

- 是 MySQL 服务器守护进程，也就是 MySQL 数据库的**实际运行**程序。
- 它负责处理数据库的存储、查询、并发访问、用户验证等核心任务。

**添加环境变量：**

将`'\path\to\mysql-8.0.31-winx64\bin\'`目录添加到 PATH 环境变量中，便于命令行操作。

**启动Mysql**

```text
net start mysql  // 启动mysql服务
net stop mysql  // 停止mysql服务
```

**修改root账户密码**

```text
mysqladmin -u root password 123456
```

本地windows下的账号:root 密码： 123456

**登录**

```text
mysql -u用户名 -p密码 [-h数据库服务器的IP地址 -P端口号] 
```

```text
mysql -uroot -p123456 
```

-h  参数不加，默认连接的是本地 127.0.0.1 的MySQL服务器

-P  参数不加，默认连接的端口号是 3306

**图形化工具**

推荐Navicat



## Mysql简介

### 通用语法

1、SQL语句可以单行或多行书写，**以分号结尾**。

2、SQL语句可以使用空格/缩进来增强语句的可读性。因为SQL语句在执行时，数据库会忽略额外的空格和换行符

```mysql
SELECT 
    name, 
    age, 
    address
FROM 
    users
WHERE 
    age > 18;
```

3、MySQL数据库的SQL语句**不区分大小写**。

4、注释：

- 单行注释：-- 注释内容   或   # 注释内容(MySQL特有)
- 多行注释： /* 注释内容 */

### 分类

| **分类** | **全称**                    | **说明**                                               |
| -------- | --------------------------- | ------------------------------------------------------ |
| DDL      | Data Definition  Language   | 数据定义语言，用来定义数据库对象(数据库，表，字段)     |
| **DML**  | Data Manipulation  Language | 数据操作语言，用来对数据库表中的数据进行**增删改**     |
| DQL      | Data Query Language         | 数据查询语言，用来**查询**数据库中表的记录             |
| DCL      | Data Control  Language      | 数据控制语言，用来创建数据库用户、控制数据库的访问权限 |

### 数据类型

**字符串类型**

CHAR(n)：声明的字段如果数据类型为char,则该字段占据的**长度固定**为声明时的值，例如：char(4),存入值 'ab',其长度仍为4.

VARCHAR(n)：varchar(100)表示最多可以存100个字符，每个字符占用的字节数取决于所使用的字符集。存储开销：除了存储实际数据外，`varchar` 类型还会额外存储 1 或 2 个字节来记录字符串的长度。

TEXT:用于存储大文本数据，存储长度远大于 VARCHAR，但不支持索引整列内容（通常索引长度有限制）。



**日期时间类型：**

| 类型         | 大小 | 范围                                       | 格式                | 描述             |
| ------------ | ---- | ------------------------------------------ | ------------------- | ---------------- |
| DATE         | 3    | 1000-01-01 至  9999-12-31                  | YYYY-MM-DD          | 日期值           |
| TIME         | 3    | -838:59:59 至  838:59:59                   | HH:MM:SS            | 时间值或持续时间 |
| **DATETIME** | 8    | 1000-01-01 00:00:00 至 9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值 |

**注意**：字符串和日期时间型数据在 SQL 语句中应包含在引号内，例如：`'2025-03-29'`、`'hello'`。



**数值类型**

| 类型        | 大小   | 有符号(SIGNED)范围                       | 无符号(UNSIGNED)范围                       | 描述               |
| ----------- | ------ | ---------------------------------------- | ------------------------------------------ | ------------------ |
| TINYINT     | 1byte  | (-128，127)                              | (0，255)                                   | 小整数值           |
| INT/INTEGER | 4bytes | (-2^31，2^31-1)                          | (0，2^32-1)                                | 大整数值           |
| FLOAT       | 4bytes | (-3.402823466 E+38，3.402823466351 E+38) | 0 和 (1.175494351  E-38，3.402823466 E+38) | 单精度浮点数值     |
| DECIMAL     |        | 依赖于M(精度)和D(标度)的值               | 依赖于M(精度)和D(标度)的值                 | 小数值(精确定点数) |

**DECIMAL(M, D)**：定点数类型，M 表示总位数，D 表示小数位数，适合存储精度要求较高的数值（如金钱）。



## DDL

### 数据库操作

**查询所有数据库：**

```mysql
show databases;
```

**创建一个itcast数据库。**

```mysql
create database itcast;
```

**切换到itcast数据**

```mysql
use itcast;
```

**查询当前正常使用的数据库：**

```mysql
select database();
```

**删除itcast数据库**

~~~mysql
drop database if exists itcast; -- itcast数据库存在时删除，不存在也不报错
~~~



### 表操作

**查询当前数据库下所有表**

```text
show tables;
```

**查看指定表的结构（字段）**

```text
desc tb_tmps；  （ tb_tmps为表名）
```

**创建表**

通常一个列定义的顺序如下：

1. 列名(字段)
2. 字段类型
3. 可选的字符集或排序规则（如果需要）
4. 约束：例如 `NOT NULL`、`UNIQUE`、`PRIMARY KEY`、`DEFAULT` 等
5. 特殊属性：例如 `AUTO_INCREMENT`
6. 注释：例如 `COMMENT '说明'`

```mysql
create table  表名(
	字段1  字段1类型 [约束]  [comment  '字段1注释' ],
	字段2  字段2类型 [约束]  [comment  '字段2注释' ],
	......
	字段n  字段n类型 [约束]  [comment  '字段n注释' ] 
) [ comment  '表注释' ] ;
```

> 注意： [ ] 中的内容为可选参数； 最后一个字段后面没有逗号

eg：

![image-20220829143005524](https://pic.bitday.top/i/2025/03/19/u7brwj-2.png)

```text
create table tb_user (
    id int comment 'ID,唯一标识',   # id是一行数据的唯一标识（不能重复）
    username varchar(20) comment '用户名',
    name varchar(10) comment '姓名',
    age int comment '年龄',
    gender char(1) comment '性别'
) comment '用户表';
```

#### 约束

| **约束**     | **描述**                                         | **关键字**  |
| ------------ | ------------------------------------------------ | ----------- |
| 非空约束     | 限制该字段值不能为null                           | not null    |
| 唯一约束     | 保证字段的所有数据都是唯一、不重复的             | unique      |
| 主键约束     | 主键是一行数据的唯一标识，要求**非空且唯一**     | primary key |
| 默认约束     | 保存数据时，如果未指定该字段值，则采用默认值     | default     |
| **外键约束** | 让两张表的数据建立连接，保证数据的一致性和完整性 | foreign key |

```mysql
CREATE TABLE tb_user (
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID,唯一标识', 
    username VARCHAR(20) NOT NULL UNIQUE COMMENT '用户名',
    name VARCHAR(10) NOT NULL COMMENT '姓名',
    age INT COMMENT '年龄',
    gender CHAR(1) DEFAULT '男' COMMENT '性别'
) COMMENT '用户表';

-- 假设我们有一个 orders 表，它将 tb_user 表的 id 字段作为外键
CREATE TABLE orders (
    order_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '订单ID',
    order_date DATE COMMENT '订单日期',
    user_id INT,
    FOREIGN KEY (user_id) REFERENCES tb_user(id) 
    ON DELETE CASCADE
    ON UPDATE CASCADE,
    COMMENT '订单表'
);
```

**foreign key:**

- 保证数据的一致性和完整性

- **`ON DELETE CASCADE`**：如果父表中的某行被删除，那么子表中所有与之关联的行也会被自动删除。

  **`ON DELETE SET NULL`**：如果父表中的某行被删除，子表中的相关外键列会被设置为 `NULL`。

  **`ON UPDATE CASCADE`**：如果父表中的外键值被更新，那么子表中的相关外键值也会自动更新。

注意：在实际的 Java 项目中，特别是在一些微服务架构或分布式系统中，通常**不直接依赖数据库中的外键约束**。相反，开发者通常会在代码中通过逻辑来确保数据的一致性和完整性。



**auto_increment:**

- 每次插入新的行记录时，数据库自动生成id字段(主键)下的值
- 具有auto_increment的数据列是一个正数序列且整型(从1开始自增)
- 不能应用于多个字段



**设计表的字段时，还应考虑：**

id：主键，唯一标志这条记录
create_time ：插入记录的时间    now()函数可以获取当前时间
update_time：最后修改记录的时间

## DML（增删改）

DML英文全称是Data Manipulation Language(数据操作语言)，用来对数据库中表的数据记录进行增、删、改操作。

- 添加数据（INSERT）
- 修改数据（UPDATE）     
- 删除数据（DELETE） 



### INSERT

insert语法：

- 向**指定**字段添加数据

  ~~~mysql
  insert into 表名 (字段名1, 字段名2) values (值1, 值2);
  ~~~

- 全部字段添加数据

  ~~~mysql
  insert into 表名 values (值1, 值2, ...);
  ~~~

- 批量添加数据（指定字段）

  ~~~mysql
  insert into 表名 (字段名1, 字段名2) values (值1, 值2), (值1, 值2);
  ~~~

- 批量添加数据（全部字段）

  ~~~mysql
  insert into 表名 values (值1, 值2, ...), (值1, 值2, ...);
  ~~~



### UPDATE

update语法：

```sql
update 表名 set 字段名1 = 值1 , 字段名2 = 值2 , .... [where 条件] ;
```

案例1：将tb_emp表中id为1的员工，姓名name字段更新为'张三'

```sql
update tb_emp set name='张三',update_time=now() where id=1;
```

案例2：将tb_emp表的所有员工入职日期更新为'2010-01-01'

```sql
update tb_emp set entrydate='2010-01-01',update_time=now();
```

**注意！**不带where会更新表中所有记录！



### DELETE

delete语法：

```SQL
delete from 表名  [where  条件] ;
```

案例1：删除tb_emp表中id为1的员工

```sql
delete from tb_emp where id = 1;
```

案例2：删除tb_emp表中所有员工(记录)

```sql
delete from tb_emp;
```

DELETE 语句不能删除某一个字段的值(可以使用UPDATE，将该字段值置为NULL即可)。



## DQL(查询)

DQL英文全称是Data Query Language(数据查询语言)，用来查询数据库表中的记录。

查询关键字：SELECT

查询操作是所有SQL语句当中最为常见，也是最为重要的操作。

### 语法

```text
SELECT
	字段列表
FROM
	表名列表                ----基本查询
WHERE
	条件列表                ----条件查询
GROUP  BY
	分组字段列表 
HAVING
	分组后条件列表            ----分组查询
ORDER BY
	排序字段列表              ----排序查询
LIMIT
	分页参数                 ----分页查询
```

### 基本查询

- 查询多个字段

  ~~~mysql
  select 字段1, 字段2, 字段3 from  表名;
  ~~~

- 查询所有字段（通配符）

  ~~~mysql
  select *  from  表名;
  ~~~

- 设置别名

  ~~~mysql
  select 字段1 [ as 别名1 ] , 字段2 [ as 别名2 ]  from  表名;
  ~~~

- 去除重复记录

  ~~~mysql
  select distinct 字段列表 from  表名;
  eg:select distinct job from tb_emp;
  ~~~

### 条件查询

| **比较运算符**       | **功能**                                 |
| -------------------- | ---------------------------------------- |
| between ...  and ... | 在某个范围之内(含最小、最大值)           |
| in(...)              | 在in之后的列表中的值，多选一             |
| like 占位符          | 模糊匹配(_匹配单个字符, %匹配任意个字符) |
| is null              | 是null                                   |
| =                    | 等于                                     |

| **逻辑运算符** | **功能**                    |
| -------------- | --------------------------- |
| and 或 &&      | 并且 (多个条件同时成立)     |
| or 或 \|\|     | 或者 (多个条件任意一个成立) |
| not 或 !       | 非 , 不是                   |



**表数据**：

| id   | name | gender | job  | entrydate  |
| ---- | ---- | ------ | ---- | ---------- |
| 1    | 张三 | 2      | 2    | 2005-04-15 |
| 2    | 李四 | 1      | 3    | 2007-07-22 |
| 3    | 王五 | 2      | 4    | 2011-09-01 |
| 4    | 赵六 | 1      | 2    | 2008-06-11 |



案例1：查询 入职时间 在 '2000-01-01' (包含) 到 '2010-01-01'(包含) 之间 且 性别为女 的员工信息

```text
select *
from tb_emp
where entrydate between '2000-01-01' and '2010-01-01'
      and gender = 2;
```

案例2：查询 职位是 2 (讲师), 3 (学工主管), 4 (教研主管) 的员工信息

```text
select *
from tb_emp
where job in (2,3,4);
```

案例3：查询 姓名 为两个字的员工信息

常见的 **LIKE** 模式匹配符包括：

​	**`%`**：表示零个或多个字符。

​	**`_`**：表示一个字符。

~~~mysql
select *
from tb_emp
where name like '__';  # 通配符 "_" 代表任意1个字符
~~~



### 聚合函数

之前我们做的查询都是**横向查询**，就是根据条件一行一行的进行判断，而使用聚合函数查询就是**纵向查询**，它是对一列的值进行计算，然后**返回一个结果值**。（将一列数据作为一个整体，进行纵向计算）

**聚合函数：**

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

语法：

~~~mysql
select  聚合函数(字段名、列名)  from  表名 ;
~~~

> 注意 : 聚合函数会忽略空值，对NULL值不作为统计。

```mysql
# count(*)  推荐此写法（MySQL底层进行了优化）
select count(*) from tb_emp;   -- 统计记录数

SELECT SUM(amount) FROM tb_sales; -- 统计amount列的总金额
```



### 分组查询

分组： 按照某一列或者某几列，把相同的数据进行合并输出。

> 分组其实就是按列进行分类(指定列下相同的数据归为一类)，然后可以对分类完的数据进行合并计算。
>
> 分组查询通常会使用**聚合函数**进行计算。

```mysql
select  字段列表  from  表名  [where 条件]  group by 分组字段名  [having 分组后过滤条件];
```

*orders表：*

| customer_id | amount |
| ----------- | ------ |
| 1           | 100    |
| 1           | 200    |
| 2           | 150    |
| 2           | 300    |

例如，假设我们有一个名为 `orders` 的表，其中包含 `customer_id` 和 `amount` 列，我们想要计算每个客户的订单总金额，可以这样写查询：

```text
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id;
```

在这个例子中，`GROUP BY customer_id` 将结果按照 `customer_id` 列的值进行分组，并对每个客户的订单金额求和，生成每个客户的总金额。

```text
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id
HAVING total_amount > specified_amount;
```

在这个查询中，`HAVING` 子句用于筛选出消费金额（`total_amount`）大于指定数目（`specified_amount`）的记录。你需要将 `specified_amount` 替换为你指定的金额数目。



**注意事项:**

​	• 分组之后，查询的字段**一般为聚合函数**和分组字段，查询其他字段无任何意义

​	• 执行顺序：where > 聚合函数 > having 



### 排序查询

语法：

```mysql
select  字段列表  
from   表名   
[where  条件列表] 
[group by  分组字段 ] 
order  by  字段1  排序方式1 , 字段2  排序方式2 … ;
```

- 排序方式：

  - ASC ：升序（默认值）

  - DESC：降序

```mysql
select id, username, password, name, gender, image, job, entrydate, create_time, update_time
from tb_emp
order by entrydate ASC; -- 按照entrydate字段下的数据进行升序排序
```



### 分页查询

```text
select  字段列表  from   表名  limit  起始索引, 每页显示记录数 ;
```

前端传过来的一般是页码，要计算起始索引

注意事项:

1. 起始索引从0开始。        计算公式 ：   起始索引 = （查询页码 - 1）* 每页显示记录数

2. 分页查询是数据库的方言，不同的数据库有不同的实现，MySQL中是LIMIT

3. 如果查询的是第一页数据，起始索引可以省略，直接简写为 limit  条数



## 多表设计

### 外键约束

外键约束的语法：

```mysql
-- 创建表时指定
CREATE TABLE child_table (
    id INT PRIMARY KEY, 
    parent_id INT,  -- 外键字段
    FOREIGN KEY (parent_id) 
        REFERENCES parent_table (id)
        ON DELETE CASCADE   -- 可选，表示父表数据删除时，子表数据也会删除
        ON UPDATE CASCADE   -- 可选，表示父表数据更新时，子表数据会同步更新
);


-- 建完表后，添加外键
ALTER TABLE child_table
ADD CONSTRAINT fk_parent_id   -- 外键约束的名称，可选
FOREIGN KEY (parent_id)
REFERENCES parent_table (id)
ON DELETE CASCADE
ON UPDATE CASCADE;
```



### 一对多

![image-20221206230156403](https://pic.bitday.top/i/2025/03/19/u7bf4a-2.png)

一对多关系实现：在数据库表中**多的一方**，添加外键字段（如dept_id），来关联'一'这方的主键(id)。



### 一对一

一对一关系表在实际开发中应用起来比较简单，通常是用来做**单表的拆分**。一对一的应用场景： 用户表=》基本信息表+身份信息表，以此来提高数据的操作效率。

![image-20221207105632634](https://pic.bitday.top/i/2025/04/01/m9jvnx-0.png)

- 基本信息：用户的ID、姓名、性别、手机号、学历
- 身份信息：民族、生日、身份证号、身份证签发机关，身份证的有效期(开始时间、结束时间)

一对一 ：在**任意一方**加入外键，关联另外一方的主键，并且设置外键为唯一的(UNIQUE)



### 多对多

多对多的关系在开发中属于也比较常见的。比如：学生和老师的关系，一个学生可以有多个授课老师，一个授课老师也可以有多个学生。

![image-20221207113341028](https://pic.bitday.top/i/2025/04/01/mbg8ad-0.png)

案例：学生与课程的关系

- 关系：一个学生可以选修多门课程，一门课程也可以供多个学生选择

- 实现关系：**建立第三张中间表**（选课表），中间表至少包含两个外键，**分别关联两方主键**



## 多表查询

### 分类

多表查询可以分为：

1. 连接查询

   - 内连接：相当于查询A、B**交集**部分数据

   ![image-20221207165446062](https://pic.bitday.top/i/2025/03/19/u7b5ng-2.png) 

2. 外连接

   - 左外连接：查询左表所有数据(包括两张表交集部分数据)

   - 右外连接：查询右表所有数据(包括两张表交集部分数据)

3. 子查询

### 内连接

隐式内连接语法：

``` mysql
select  字段列表   from   表1 , 表2   where  条件 ... ;
```

显式内连接语法：

``` mysql
select  字段列表   from   表1  [ inner ]  join 表2  on  连接条件 ... ;
```

[inner]可省略

案例：查询员工的姓名及所属的部门名称

- 隐式内连接实现

~~~mysql
select tb_emp.name , tb_dept.name -- 分别查询两张表中的数据
from tb_emp , tb_dept -- 关联两张表
where tb_emp.dept_id = tb_dept.id; -- 消除笛卡尔积
~~~

- 显示内连接

```text
select tb_emp.name , tb_dept.name
from tb_emp inner join tb_dept
on tb_emp.dept_id = tb_dept.id;
```



### 外连接

左外连接语法结构：

```mysql
select  字段列表   from   表1  left  [ outer ]  join 表2  on  连接条件 ... ;
```

> 左外连接相当于查询表1(左表)的所有数据，当然也包含表1和表2交集部分的数据。

右外连接语法结构：

```mysql
select  字段列表   from   表1  right  [ outer ]  join 表2  on  连接条件 ... ;
```

> 右外连接相当于查询表2(右表)的**所有**数据，当然也包含表1和表2交集部分的数据。



案例：查询部门表中所有部门的名称, 和对应的员工名称 

```text
-- 左外连接：以left join关键字左边的表为主表，查询主表中所有数据，以及和主表匹配的右边表中的数据
select emp.name , dept.name
from tb_emp AS emp left join tb_dept AS dept 
     on emp.dept_id = dept.id;
```

![image-20221207181204792](https://pic.bitday.top/i/2025/04/01/mgov75-0.png)



### 子查询

SQL语句中嵌套select语句，称为嵌套查询，又称子查询。

```sql
SELECT  *  FROM   t1   WHERE  column1 =  ( SELECT  column1  FROM  t2 ... );
```

> 子查询外部的语句可以是insert / update / delete / select 的任何一个，最常见的是 select。

#### 标量子查询

子查询返回的结果是单个值(数字、字符串、日期等)，最简单的形式，这种子查询称为标量子查询。

常用的操作符： =   <>   >    >=    <   <=   

案例1：查询"教研部"的所有员工信息

> 可以将需求分解为两步：
>
> 1. 查询 "教研部" 部门ID
> 2. 根据 "教研部" 部门ID，查询员工信息

```mysql
-- 1.查询"教研部"部门ID
select id from tb_dept where name = '教研部';    #查询结果：2
-- 2.根据"教研部"部门ID, 查询员工信息
select * from tb_emp where dept_id = 2;

-- 合并出上两条SQL语句
select * from tb_emp where dept_id = (select id from tb_dept where name = '教研部');
```

#### 列子查询

子查询返回的结果是**一列(可以是多行，即多条记录)**，这种子查询称为列子查询。

常用的操作符：

| **操作符** | **描述**                     |
| ---------- | ---------------------------- |
| IN         | 在指定的集合范围之内，多选一 |
| NOT IN     | 不在指定的集合范围之内       |

案例：查询"教研部"和"咨询部"的所有员工信息

> 分解为以下两步：
>
> 1. 查询 "销售部" 和 "市场部" 的部门ID
> 2. 根据部门ID, 查询员工信息

```mysql
-- 1.查询"销售部"和"市场部"的部门ID
select id from tb_dept where name = '教研部' or name = '咨询部';    #查询结果：3,2
-- 2.根据部门ID, 查询员工信息
select * from tb_emp where dept_id in (3,2);

-- 合并以上两条SQL语句
select * from tb_emp where dept_id in (select id from tb_dept where name = '教研部' or name = '咨询部');
```

#### 行子查询

子查询返回的结果是**一行(可以是多列，即多字段)**，这种子查询称为行子查询。

常用的操作符：= 、<> 、IN 、NOT IN

案例：查询与"韦一笑"的入职日期及职位都相同的员工信息 

> 可以拆解为两步进行： 
>
> 1. 查询 "韦一笑" 的入职日期 及 职位
> 2. 查询与"韦一笑"的入职日期及职位相同的员工信息 

```mysql
-- 查询"韦一笑"的入职日期 及 职位
select entrydate , job from tb_emp where name = '韦一笑';  #查询结果： 2007-01-01 , 2
-- 查询与"韦一笑"的入职日期及职位相同的员工信息
select * from tb_emp where (entrydate,job) = ('2007-01-01',2);

-- 合并以上两条SQL语句
select * from tb_emp where (entrydate,job) = (select entrydate , job from tb_emp where name = '韦一笑');
```

#### 表子查询

子查询返回的结果是多行多列，常作为临时表，这种子查询称为表子查询。

案例：查询入职日期是 "2006-01-01" 之后的员工信息 , 及其部门信息

> 分解为两步执行：
>
> 1. 查询入职日期是 "2006-01-01" 之后的员工信息
> 2. 基于查询到的员工信息，在查询对应的部门信息

~~~mysql
select * from emp where entrydate > '2006-01-01';

select e.*, d.* from (select * from emp where entrydate > '2006-01-01') e left join dept d on e.dept_id = d.id ;
~~~



## 事务

简而言之：事务是一组操作的集合，它是一个不可分割的工作单位。事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求，即这些操作要么同时成功，要么同时失败。

手动提交事务使用步骤：

- 第1种情况：开启事务  =>  执行SQL语句   =>  成功  =>  提交事务
- 第2种情况：开启事务  =>  执行SQL语句   =>  失败  =>  回滚事务

```mysql
-- 开启事务
start transaction ;

-- 删除学工部
delete from tb_dept where id = 1;

-- 删除学工部的员工
delete from tb_emp where dept_id = 1;
```

- 上述的这组SQL语句，如果如果执行成功，则提交事务

```sql
-- 提交事务 (成功时执行)
commit ;
```

- 上述的这组SQL语句，如果如果执行失败，则回滚事务

```sql
-- 回滚事务 (出错时执行)
rollback ;
```

面试题：事务有哪些特性？

- 原子性（Atomicity）：事务是不可分割的最小单元，要么全部成功，要么全部失败。
- 一致性（Consistency）：事务完成时，必须使所有的数据都保持一致状态。（部门和该部门下的员工数据全部删除）
- 隔离性（Isolation）：数据库系统提供的隔离机制，保证事务在不受外部并发操作影响的独立环境下运行（事务还没commit，那么别的窗口就看不到该修改 ）。
- 持久性（Durability）：事务一旦提交或回滚，它对数据库中的数据的改变就是永久的。

> 事务的四大特性简称为：ACID



## 索引

索引(index)：是帮助数据库高效获取数据的数据结构 。

**创建索引**

```text
-- 添加索引
create index idx_sku_sn on tb_sku (sn);  #在添加索引时，也需要消耗时间

-- 查询数据（使用了索引）
select * from tb_sku where sn = '100000003145008';
```

**查看索引**

~~~mysql
show  index  from  表名;
~~~

案例：查询 tb_emp 表的索引信息

~~~mysql
show  index  from  tb_emp;
~~~

**删除索引**

~~~mysql
drop  index  索引名  on  表名;
~~~

案例：删除 tb_emp 表中name字段的索引

~~~mysql
drop index idx_emp_name on tb_emp;
~~~

优点：

1. 提高数据查询的效率，降低数据库的IO成本。
2. 通过索引列对数据进行排序，降低数据排序的成本，降低CPU消耗。

缺点：

1. 索引会占用存储空间。
2. 索引大大提高了查询效率，同时却也降低了insert、update、delete的效率。

因为插入一条数据，要重新维护索引结构



**注意事项：**

- 主键字段，在建表时，会自动创建主键索引  （primarily key）

- 添加唯一约束时，数据库实际上会添加唯一索引 （unique约束）

### 结构

musql默认采用B+树来作索引

采用二叉搜索树或者是红黑树来作为索引的结构有什么问题？

<details>
    <summary>答案</summary>
    最大的问题就是在数据量大的情况下，树的层级比较深，会影响检索速度。因为不管是二叉搜索数还是红黑数，一个节点下面只能有两个子节点。此时在数据量大的情况下，就会造成数的高度比较高，树的高度一旦高了，检索速度就会降低。
</details>

> 说明：如果数据结构是红黑树，那么查询1000万条数据，根据计算树的高度大概是23左右，这样确实比之前的方式快了很多，但是如果高并发访问，那么一个用户有可能需要23次磁盘IO，那么100万用户，那么会造成效率极其低下。所以为了减少红黑树的高度，那么就得增加树的宽度，就是不再像红黑树一样每个节点只能保存一个数据，可以引入另外一种数据结构，一个节点可以保存多个数据，这样宽度就会增加从而降低树的高度。这种数据结构例如BTree就满足。

下面我们来看看B+Tree(多路平衡搜索树)结构中如何避免这个问题：

![image-20221208181315728](https://pic.bitday.top/i/2025/03/19/u7bnxa-2.png)

B+Tree结构：

- 每一个节点，可以存储多个key（有n个key，就有n个指针）
- 节点分为：叶子节点、非叶子节点
  - 叶子节点，就是最后一层子节点，所有的数据都存储在叶子节点上
  - 非叶子节点，不是树结构最下面的节点，用于索引数据，存储的的是：key+指针
- 为了提高范围查询效率，叶子节点形成了一个双向链表，便于数据的排序及区间范围查询

