md_files/自学/Mysql数据库.md

984 lines
28 KiB
Markdown
Raw Normal View History

2025-03-18 12:46:59 +08:00
# Mysql数据库
## 安装启动Mysql
**mysql**
- 是 MySQL 的命令行**客户端**工具,用于连接、查询和管理 MySQL 数据库。
- 你可以通过它来执行 SQL 命令、查看数据和管理数据库。
**mysqld**
- 是 MySQL 服务器守护进程,也就是 MySQL 数据库的**实际运行**程序。
- 它负责处理数据库的存储、查询、并发访问、用户验证等核心任务。
**添加环境变量:**
`'\path\to\mysql-8.0.31-winx64\bin\'`目录添加到 PATH 环境变量中,便于命令行操作。
**启动Mysql**
2025-03-18 12:46:59 +08:00
```text
2025-03-18 12:46:59 +08:00
net start mysql // 启动mysql服务
net stop mysql // 停止mysql服务
```
**修改root账户密码**
2025-03-18 12:46:59 +08:00
```text
2025-03-18 12:46:59 +08:00
mysqladmin -u root password 123456
```
2025-03-18 17:35:22 +08:00
本地windows下的账号:root 密码: 123456
**登录**
2025-03-18 12:46:59 +08:00
```text
2025-03-18 12:46:59 +08:00
mysql -u用户名 -p密码 [-h数据库服务器的IP地址 -P端口号]
```
```text
2025-03-18 12:46:59 +08:00
mysql -uroot -p123456
```
-h 参数不加,默认连接的是本地 127.0.0.1 的MySQL服务器
-P 参数不加,默认连接的端口号是 3306
**图形化工具**
推荐Navicat
2025-03-18 12:46:59 +08:00
## Mysql简介
### 通用语法
1、SQL语句可以单行或多行书写**以分号结尾**。
2025-03-18 12:46:59 +08:00
2、SQL语句可以使用空格/缩进来增强语句的可读性。因为SQL语句在执行时数据库会忽略额外的空格和换行符
2025-03-18 12:46:59 +08:00
```mysql
SELECT
name,
age,
address
FROM
users
WHERE
age > 18;
```
3、MySQL数据库的SQL语句**不区分大小写**。
2025-03-18 12:46:59 +08:00
4、注释
- 单行注释:-- 注释内容 或 # 注释内容(MySQL特有)
- 多行注释: /* 注释内容 */
### 分类
| **分类** | **全称** | **说明** |
| -------- | --------------------------- | ------------------------------------------------------ |
| DDL | Data Definition Language | 数据定义语言,用来定义数据库对象(数据库,表,字段) |
| **DML** | Data Manipulation Language | 数据操作语言,用来对数据库表中的数据进行**增删改** |
| DQL | Data Query Language | 数据查询语言,用来**查询**数据库中表的记录 |
2025-03-18 12:46:59 +08:00
| DCL | Data Control Language | 数据控制语言,用来创建数据库用户、控制数据库的访问权限 |
### 数据类型
**字符串类型**
CHAR(n)声明的字段如果数据类型为char,则该字段占据的**长度固定**为声明时的值例如char(4),存入值 'ab',其长度仍为4.
2025-03-18 12:46:59 +08:00
VARCHAR(n)varchar(100)表示最多可以存100个字符每个字符占用的字节数取决于所使用的字符集。存储开销除了存储实际数据外`varchar` 类型还会额外存储 1 或 2 个字节来记录字符串的长度。
2025-03-18 12:46:59 +08:00
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 | (-128127) | (0255) | 小整数值 |
| INT/INTEGER | 4bytes | (-2^312^31-1) | (02^32-1) | 大整数值 |
| FLOAT | 4bytes | (-3.402823466 E+383.402823466351 E+38) | 0 和 (1.175494351 E-383.402823466 E+38) | 单精度浮点数值 |
| DECIMAL | | 依赖于M(精度)和D(标度)的值 | 依赖于M(精度)和D(标度)的值 | 小数值(精确定点数) |
**DECIMAL(M, D)**定点数类型M 表示总位数D 表示小数位数,适合存储精度要求较高的数值(如金钱)。
2025-03-18 12:46:59 +08:00
## DDL
### 数据库操作
**查询所有数据库:**
```mysql
show databases;
```
**创建一个itcast数据库。**
```mysql
create database itcast;
```
**切换到itcast数据**
```mysql
use itcast;
```
**查询当前正常使用的数据库:**
2025-03-18 12:46:59 +08:00
```mysql
select database();
```
**删除itcast数据库**
~~~mysql
drop database if exists itcast; -- itcast数据库存在时删除不存在也不报错
~~~
2025-03-18 12:46:59 +08:00
### 表操作
**查询当前数据库下所有表**
```text
2025-03-18 12:46:59 +08:00
show tables;
```
**查看指定表的结构(字段)**
```text
2025-03-18 12:46:59 +08:00
desc tb_tmps tb_tmps为表名
```
**创建表**
通常一个列定义的顺序如下:
1. 列名(字段)
2. 字段类型
3. 可选的字符集或排序规则(如果需要)
4. 约束:例如 `NOT NULL``UNIQUE``PRIMARY KEY``DEFAULT`
5. 特殊属性:例如 `AUTO_INCREMENT`
6. 注释:例如 `COMMENT '说明'`
2025-03-18 12:46:59 +08:00
```mysql
create table 表名(
字段1 字段1类型 [约束] [comment '字段1注释' ],
字段2 字段2类型 [约束] [comment '字段2注释' ],
2025-03-18 12:46:59 +08:00
......
字段n 字段n类型 [约束] [comment '字段n注释' ]
) [ comment '表注释' ] ;
2025-03-18 12:46:59 +08:00
```
> 注意: [ ] 中的内容为可选参数; 最后一个字段后面没有逗号
eg
2025-03-19 18:31:37 +08:00
![image-20220829143005524](https://pic.bitday.top/i/2025/03/19/u7brwj-2.png)
2025-03-18 12:46:59 +08:00
```text
2025-03-18 12:46:59 +08:00
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 |
2025-03-18 12:46:59 +08:00
| 默认约束 | 保存数据时,如果未指定该字段值,则采用默认值 | 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 '订单表'
);
2025-03-18 12:46:59 +08:00
```
**foreign key:**
- 保证数据的一致性和完整性
- **`ON DELETE CASCADE`**:如果父表中的某行被删除,那么子表中所有与之关联的行也会被自动删除。
**`ON DELETE SET NULL`**:如果父表中的某行被删除,子表中的相关外键列会被设置为 `NULL`
**`ON UPDATE CASCADE`**:如果父表中的外键值被更新,那么子表中的相关外键值也会自动更新。
注意:在实际的 Java 项目中,特别是在一些微服务架构或分布式系统中,通常**不直接依赖数据库中的外键约束**。相反,开发者通常会在代码中通过逻辑来确保数据的一致性和完整性。
**auto_increment:**
2025-03-18 12:46:59 +08:00
- 每次插入新的行记录时数据库自动生成id字段(主键)下的值
- 具有auto_increment的数据列是一个正数序列且整型(从1开始自增)
- 不能应用于多个字段
2025-03-18 12:46:59 +08:00
**设计表的字段时,还应考虑:**
id主键唯一标志这条记录
create_time :插入记录的时间 now()函数可以获取当前时间
update_time最后修改记录的时间
## DML增删改
DML英文全称是Data Manipulation Language(数据操作语言),用来对数据库中表的数据记录进行增、删、改操作。
- 添加数据INSERT
- 修改数据UPDATE
- 删除数据DELETE
2025-03-18 12:46:59 +08:00
### 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, ...);
~~~
2025-03-18 12:46:59 +08:00
### 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会更新表中所有记录
2025-03-18 12:46:59 +08:00
### 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即可)。
2025-03-18 12:46:59 +08:00
## DQL(查询)
DQL英文全称是Data Query Language(数据查询语言),用来查询数据库表中的记录。
查询关键字SELECT
查询操作是所有SQL语句当中最为常见也是最为重要的操作。
### 语法
```text
2025-03-18 12:46:59 +08:00
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'(包含) 之间 且 性别为女 的员工信息
2025-03-18 12:46:59 +08:00
```text
2025-03-18 12:46:59 +08:00
select *
from tb_emp
where entrydate between '2000-01-01' and '2010-01-01'
and gender = 2;
```
案例2查询 职位是 2 (讲师), 3 (学工主管), 4 (教研主管) 的员工信息
2025-03-18 12:46:59 +08:00
```text
2025-03-18 12:46:59 +08:00
select *
from tb_emp
where job in (2,3,4);
```
案例3查询 姓名 为两个字的员工信息
常见的 **LIKE** 模式匹配符包括:
**`%`**:表示零个或多个字符。
**`_`**:表示一个字符。
2025-03-18 12:46:59 +08:00
~~~mysql
select *
from tb_emp
where name like '__'; # 通配符 "_" 代表任意1个字符
~~~
### 聚合函数
之前我们做的查询都是**横向查询**,就是根据条件一行一行的进行判断,而使用聚合函数查询就是**纵向查询**,它是对一列的值进行计算,然后**返回一个结果值**。(将一列数据作为一个整体,进行纵向计算)
**聚合函数:**
| **函数** | **功能** |
| -------- | -------- |
| count | 统计数量 |
| max | 最大值 |
| min | 最小值 |
| avg | 平均值 |
| sum | 求和 |
2025-03-18 12:46:59 +08:00
语法:
~~~mysql
select 聚合函数(字段名、列名) from 表名 ;
2025-03-18 12:46:59 +08:00
~~~
> 注意 : 聚合函数会忽略空值对NULL值不作为统计。
```mysql
2025-03-18 12:46:59 +08:00
# count(*) 推荐此写法MySQL底层进行了优化
select count(*) from tb_emp; -- 统计记录数
SELECT SUM(amount) FROM tb_sales; -- 统计amount列的总金额
2025-03-18 12:46:59 +08:00
```
### 分组查询
分组: 按照某一列或者某几列,把相同的数据进行合并输出。
> 分组其实就是按列进行分类(指定列下相同的数据归为一类),然后可以对分类完的数据进行合并计算。
>
> 分组查询通常会使用**聚合函数**进行计算。
```mysql
2025-03-18 12:46:59 +08:00
select 字段列表 from 表名 [where 条件] group by 分组字段名 [having 分组后过滤条件];
```
*orders表*
| customer_id | amount |
| ----------- | ------ |
| 1 | 100 |
| 1 | 200 |
| 2 | 150 |
| 2 | 300 |
2025-03-18 12:46:59 +08:00
例如,假设我们有一个名为 `orders` 的表,其中包含 `customer_id``amount` 列,我们想要计算每个客户的订单总金额,可以这样写查询:
```text
2025-03-18 12:46:59 +08:00
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id;
```
在这个例子中,`GROUP BY customer_id` 将结果按照 `customer_id` 列的值进行分组,并对每个客户的订单金额求和,生成每个客户的总金额。
```text
2025-03-18 12:46:59 +08:00
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` 替换为你指定的金额数目。
**注意事项:**
• 分组之后,查询的字段**一般为聚合函数**和分组字段,查询其他字段无任何意义
2025-03-18 12:46:59 +08:00
• 执行顺序where > 聚合函数 > having
2025-03-18 12:46:59 +08:00
### 排序查询
语法:
```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字段下的数据进行升序排序
```
2025-03-18 12:46:59 +08:00
### 分页查询
```text
2025-03-18 12:46:59 +08:00
select 字段列表 from 表名 limit 起始索引, 每页显示记录数 ;
```
前端传过来的一般是页码,要计算起始索引
注意事项:
1. 起始索引从0开始。 计算公式 起始索引 = (查询页码 - 1* 每页显示记录数
2. 分页查询是数据库的方言不同的数据库有不同的实现MySQL中是LIMIT
3. 如果查询的是第一页数据,起始索引可以省略,直接简写为 limit 条数
2025-03-18 12:46:59 +08:00
## 多表设计
### 外键约束
外键约束的语法:
```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 -- 可选,表示父表数据更新时,子表数据会同步更新
2025-03-18 12:46:59 +08:00
);
-- 建完表后,添加外键
ALTER TABLE child_table
ADD CONSTRAINT fk_parent_id -- 外键约束的名称,可选
FOREIGN KEY (parent_id)
REFERENCES parent_table (id)
ON DELETE CASCADE
ON UPDATE CASCADE;
2025-03-18 12:46:59 +08:00
```
### 一对多
2025-03-19 18:31:37 +08:00
![image-20221206230156403](https://pic.bitday.top/i/2025/03/19/u7bf4a-2.png)
2025-03-18 12:46:59 +08:00
一对多关系实现:在数据库表中**多的一方**添加外键字段如dept_id来关联'一'这方的主键(id)。
2025-03-18 12:46:59 +08:00
### 一对一
一对一关系表在实际开发中应用起来比较简单,通常是用来做**单表的拆分**。一对一的应用场景: 用户表=》基本信息表+身份信息表,以此来提高数据的操作效率。
![image-20221207105632634](https://pic.bitday.top/i/2025/04/01/m9jvnx-0.png)
2025-03-18 12:46:59 +08:00
- 基本信息用户的ID、姓名、性别、手机号、学历
- 身份信息:民族、生日、身份证号、身份证签发机关,身份证的有效期(开始时间、结束时间)
一对一 :在**任意一方**加入外键,关联另外一方的主键,并且设置外键为唯一的(UNIQUE)
2025-03-18 12:46:59 +08:00
### 多对多
多对多的关系在开发中属于也比较常见的。比如:学生和老师的关系,一个学生可以有多个授课老师,一个授课老师也可以有多个学生。
![image-20221207113341028](https://pic.bitday.top/i/2025/04/01/mbg8ad-0.png)
2025-03-18 12:46:59 +08:00
案例:学生与课程的关系
- 关系:一个学生可以选修多门课程,一门课程也可以供多个学生选择
- 实现关系:**建立第三张中间表**(选课表),中间表至少包含两个外键,**分别关联两方主键**
2025-03-18 12:46:59 +08:00
## 多表查询
### 分类
多表查询可以分为:
1. 连接查询
- 内连接相当于查询A、B**交集**部分数据
2025-03-18 12:46:59 +08:00
2025-03-19 18:31:37 +08:00
![image-20221207165446062](https://pic.bitday.top/i/2025/03/19/u7b5ng-2.png)
2025-03-18 12:46:59 +08:00
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
2025-03-18 12:46:59 +08:00
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交集部分的数据。
2025-03-18 12:46:59 +08:00
案例:查询部门表中所有部门的名称, 和对应的员工名称
2025-03-18 12:46:59 +08:00
```text
-- 左外连接以left join关键字左边的表为主表查询主表中所有数据以及和主表匹配的右边表中的数据
select emp.name , dept.name
from tb_emp AS emp left join tb_dept AS dept
2025-03-18 12:46:59 +08:00
on emp.dept_id = dept.id;
```
![image-20221207181204792](https://pic.bitday.top/i/2025/04/01/mgov75-0.png)
2025-03-18 12:46:59 +08:00
### 子查询
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 ;
~~~
2025-03-18 12:46:59 +08:00
## 事务
简而言之:事务是一组操作的集合,它是一个不可分割的工作单位。事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
手动提交事务使用步骤:
- 第1种情况开启事务 => 执行SQL语句 => 成功 => 提交事务
- 第2种情况开启事务 => 执行SQL语句 => 失败 => 回滚事务
```mysql
2025-03-18 12:46:59 +08:00
-- 开启事务
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
2025-03-18 12:46:59 +08:00
## 索引
索引(index):是帮助数据库高效获取数据的数据结构 。
**创建索引**
```text
2025-03-18 12:46:59 +08:00
-- 添加索引
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(多路平衡搜索树)结构中如何避免这个问题:
2025-03-19 18:31:37 +08:00
![image-20221208181315728](https://pic.bitday.top/i/2025/03/19/u7bnxa-2.png)
2025-03-18 12:46:59 +08:00
B+Tree结构
- 每一个节点可以存储多个key有n个key就有n个指针
- 节点分为:叶子节点、非叶子节点
- 叶子节点,就是最后一层子节点,所有的数据都存储在叶子节点上
- 非叶子节点不是树结构最下面的节点用于索引数据存储的的是key+指针
- 为了提高范围查询效率,叶子节点形成了一个双向链表,便于数据的排序及区间范围查询