# Mysql数据库 ## 安装启动Mysql ### 启动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 ## Mysql简介 ### 通用语法 1、SQL语句可以单行或多行书写,以分号结尾。 2、SQL语句可以使用空格/缩进来增强语句的可读性。 3、MySQL数据库的SQL语句不区分大小写。 4、注释: - 单行注释:-- 注释内容 或 # 注释内容(MySQL特有) - 多行注释: /* 注释内容 */ ### 分类 | **分类** | **全称** | **说明** | | -------- | --------------------------- | ------------------------------------------------------ | | DDL | Data Definition Language | 数据定义语言,用来定义数据库对象(数据库,表,字段) | | DML | Data Manipulation Language | 数据操作语言,用来对数据库表中的数据进行增删改 | | DQL | Data Query Language | 数据查询语言,用来查询数据库中表的记录 | | DCL | Data Control Language | 数据控制语言,用来创建数据库用户、控制数据库的访问权限 | ### 数据类型 char:声明的字段如果数据类型为char,则该字段占据的长度固定为声明时的值,例如:char(4),存入值 'ab',其长度仍为4. varchar:声明字段时,字段占据的实际长度等于存储内容的实际长度+记录长度的字节(一般是一个字节或者两个字节)例如:varchar(100)表示可以存100,但是存储值'ab'时,占用长度是3字节。 日期时间类型: | 类型 | 大小 | 范围 | 格式 | 描述 | | -------- | ---- | ------------------------------------------ | ------------------- | ---------------- | | 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 | 混合日期和时间值 | **字符串和日期型数据都应包含在引号中** ## 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为表名) ``` **创建表** ```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 | ```text 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 '用户表'; ``` 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 或 ! | 非 , 不是 | 案例:查询 入职时间 在 '2000-01-01' (包含) 到 '2010-01-01'(包含) 之间 且 性别为女 的员工信息 ```text select * from tb_emp where entrydate between '2000-01-01' and '2010-01-01' and gender = 2; ``` 案例8:查询 职位是 2 (讲师), 3 (学工主管), 4 (教研主管) 的员工信息 ```text select * from tb_emp where job in (2,3,4); ``` 案例9:查询 姓名 为两个字的员工信息 ~~~mysql select * from tb_emp where name like '__'; # 通配符 "_" 代表任意1个字符 ~~~ ### 聚合函数 之前我们做的查询都是横向查询,就是根据条件一行一行的进行判断,而使用聚合函数查询就是**纵向查询**,它是对一列的值进行计算,然后返回一个结果值。(将一列数据作为一个整体,进行纵向计算) 语法: ~~~mysql select 聚合函数(字段列表) from 表名 ; ~~~ > 注意 : 聚合函数会忽略空值,对NULL值不作为统计。 ```text # count(*) 推荐此写法(MySQL底层进行了优化) select count(*) from tb_emp; ``` ### 分组查询 分组: 按照某一列或者某几列,把相同的数据进行合并输出。 > 分组其实就是按列进行分类(指定列下相同的数据归为一类),然后可以对分类完的数据进行合并计算。 > > 分组查询通常会使用**聚合函数**进行计算。 ```text select 字段列表 from 表名 [where 条件] group by 分组字段名 [having 分组后过滤条件]; ``` 例如,假设我们有一个名为 `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:降序 ### 分页查询 ```text select 字段列表 from 表名 limit 起始索引, 每页显示记录数 ; ``` 前端传过来的一般是页码,要计算起始索引 注意事项: 1. 起始索引从0开始。 计算公式 : 起始索引 = (查询页码 - 1)* 每页显示记录数 2. 分页查询是数据库的方言,不同的数据库有不同的实现,MySQL中是LIMIT 3. 如果查询的是第一页数据,起始索引可以省略,直接简写为 limit 条数 ## 多表设计 ### 外键约束 外键约束的语法: ```mysql -- 创建表时指定 create table 表名( 字段名 数据类型, ... [constraint] [外键名称] foreign key (外键字段名) references 主表 (主键名) ); -- 建完表后,添加外键 alter table 表名 add constraint 外键名称 foreign key(外键字段名) references 主表(主表列名); ``` ### 一对多 ![image-20221206230156403](https://pic.bitday.top/i/2025/03/19/u7bf4a-2.png) **一对多关系实现:在数据库表中多的一方,添加外键字段,来关联'一'这方的主键。** ### 一对一 一对一关系表在实际开发中应用起来比较简单,通常是用来做单表的拆分。一对一的应用场景: 用户表=》基本信息表+身份信息表 - 基本信息:用户的ID、姓名、性别、手机号、学历 - 身份信息:民族、生日、身份证号、身份证签发机关,身份证的有效期(开始时间、结束时间) **一对一 :在任意一方加入外键,关联另外一方的主键,并且设置外键为唯一的(UNIQUE)** ### 多对多 多对多的关系在开发中属于也比较常见的。比如:学生和老师的关系,一个学生可以有多个授课老师,一个授课老师也可以有多个学生。 案例:学生与课程的关系 - 关系:一个学生可以选修多门课程,一门课程也可以供多个学生选择 - 实现关系:建立第三张中间表(选课表),中间表至少包含两个外键,分别关联两方主键 ## 多表查询 ### 分类 多表查询可以分为: 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 连接条件 ... ; ``` ```text -- 右外连接 select dept.name , emp.name from tb_emp AS emp right join tb_dept AS dept on emp.dept_id = dept.id; ``` ![image-20240306190305575](https://pic.bitday.top/i/2025/03/19/u7c5gf-2.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语句 => 失败 => 回滚事务 ` ```text -- 开启事务 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+树来作索引 采用二叉搜索树或者是红黑树来作为索引的结构有什么问题?
答案 最大的问题就是在数据量大的情况下,树的层级比较深,会影响检索速度。因为不管是二叉搜索数还是红黑数,一个节点下面只能有两个子节点。此时在数据量大的情况下,就会造成数的高度比较高,树的高度一旦高了,检索速度就会降低。
> 说明:如果数据结构是红黑树,那么查询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+指针 - 为了提高范围查询效率,叶子节点形成了一个双向链表,便于数据的排序及区间范围查询