基本概念
在学习MySQL之前,我们先来了解数据库:英文为 DataBase,简称DB,它是存储和管理数据的仓库。
数据是存储在数据库中的,那我们要如何来操作数据库以及数据库中所存放的数据呢?
那这里呢,会涉及到一个系统,那就是数据库管理系统:数据库管理系统(DataBase Management System,简称DBMS),是操作和管理数据库的大型软件。
那具体DBMS是怎么操控数据库的呢?
我们会给DBMS发送一条指令,告诉这个软件我们要执行的是什么样的操作,要对哪个数据进行操作。而这个指令就是SQL语句:SQL(Structured Query Language,简称SQL):结构化查询语言,它是操作关系型数据库的编程语言,定义了一套操作关系型数据库的统一标准。
在我们搞清楚这些最基本的概念之后,我们就可以开始学习MySQL了。
数据模型
关系型数据库(RDBMS)
建立在关系模型基础上,由多张相互连接的二维表组成的数据库。而所谓二维表,指的是由行和列组成的表:
二维表的优点:
- 使用表存储数据,格式统一,便于维护
- 使用SQL语言操作,标准统一,使用方便,可用于复杂查询
结论:基于二维表存储数据的数据库就成为关系型数据库,不是基于二维表存储数据的数据库,就是非关系型数据库(比如后面要学习的Redis,就属于非关系型数据库)。
数据模型
- 通过MySQL客户端连接数据库管理系统DBMS,然后通过DBMS操作数据库。
- 使用MySQL客户端,向数据库管理系统发送一条SQL语句,由数据库管理系统根据SQL语句指令去操作数据库中的表结构及数据。
- 一个数据库服务器中可以创建多个数据库,一个数据库中也可以包含多张表,而一张表中又可以包含多行记录。
如图:
所以在Mysql数据库服务器当中存储数据,你需要:
- 先去创建数据库(可以创建多个数据库,之间是相互独立的)。
- 在数据库下再去创建数据表(一个数据库下可以创建多张表)。
- 再将数据存放在数据表中(一张表可以存储多行数据)。
SQL
SQL:结构化查询语言。一门操作关系型数据库的编程语言,定义操作所有关系型数据库的统一标准。SQL语句根据其功能被分为四大类:DDL、DML、DQL、DCL (前三个更重要)
| 分类 | 全称 | 说明 |
|---|---|---|
| DDL | Data Definition Language | 数据定义语言,用来定义数据库对象(数据库,表,字段) |
| DML | Data Manipulation Language | 数据操作语言,用来对数据库表中的数据进行增删改 |
| DQL | Data Query Language | 数据查询语言,用来查询数据库中表的记录 |
| DCL | Data Control Language | 数据控制语言,用来创建数据库用户、控制数据库的访问权限 |
接下来就来一个个解析:
DDL
数据库操作
我们在进行数据库设计时,需要使用到刚才所介绍SQL分类中的DDL语句。
DDL英文全称是Data Definition Language(数据定义语言),用来定义数据库对象(数据库、表)。
DDL中数据库的常见操作:查询、创建、使用、删除
查询
- 查询所有数据库
1 | show databases; |
- 查询当前数据库
1 | select database(); |
我们要操作某一个数据库,必须要切换到对应的数据库中。
也可以借助图形化界面工具DataGrip直接看见:
- 创建一个db01数据库。
1 | create database db01; |
注意:在同一个数据库服务器中,不能创建两个名称相同的数据库,否则将会报错
可以使用if not exists来避免这个问题
1 | //数据库不存在,则创建该数据库;如果存在则不创建 |
使用(切换)
案例:切换到itcast数据
1 | use db01; |
删除
语法:
1 | drop database [ if exists ] 数据库名 ; |
如果删除一个不存在的数据库,将会报错,可以加上参数 if exists ,如果数据库存在,再执行删除,否则不执行删除。
删除db01数据库
1 | drop database if exists db01; |
数据类型
选择原则:在满足业务需求的前提下, 尽可能选择占用磁盘空间小的数据类型
数值类型
| 类型 | 大小(byte) | 描述 | 备注 |
|---|---|---|---|
| tinyint | 1 | 小整数值 | |
| smallint | 2 | 大整数值 | |
| mediumint | 3 | 大整数值 | |
| int | 4 | 大整数值 | |
| bigint | 8 | 极大整数值 | |
| float | 4 | 单精度浮点数值 | float(5,2):5表示整个数字长度,2 表示小数位个数 |
| double | 8 | 双精度浮点数值 | double(5,2):5表示整个数字长度,2 表示小数位个数 |
| decimal | 小数值(精度更高) | decimal(5,2):5表示整个数字长度,2 表示小数位个数 |
字符串类型
| 类型 | 大小 | 描述 |
|---|---|---|
| char | 0-255 bytes | 定长字符串 |
| varchar | 0-65535 bytes | 变长字符串 |
| tinyblob | 0-255 bytes | 不超过255个字符的二进制数据 |
| tinytext | 0-255 bytes | 短文本字符串 |
| blob | 0-65 535 bytes | 二进制形式的长文本数据 |
| text | 0-65 535 bytes | 长文本数据 |
| mediumblob | 0-16 777 215 bytes | 二进制形式的中等长度文本数据 |
| mediumtext | 0-16 777 215 bytes | 中等长度文本数据 |
| longblob | 0-4 294 967 295 bytes | 二进制形式的极大文本数据 |
| longtext | 0-4 294 967 295 bytes | 极大文本数据 |
- blob存二进制,可以存音频视频,但用的少,视频等资源一般用文件系统管理,方便管理
char,vchar详解
CHAR是固定长度的字符串类型,定义时需要指定固定长度,存储时会在末尾补足空格。CHAR适合存储长度固定的数据,如固 定长度的代码、状态等,存储空间固定,对于短字符串效率较高。
VARCHAR是可变长度的字符串类型,定义时需要指定最大长度,实际存储时根据实际长度占用存储空间。VARCHAR适合存储长度可变的数据,如用户输入的文本、备注等,节约存储空间。
| 类型 | 优势 | 劣势 |
|---|---|---|
| char | 性能略高 | 浪费磁盘空间 |
| vchar | 节约磁盘空间 | 性能略低 |
- 使用原则:固定长度使用char,不固定用vchar
日期时间类型
| 类型 | 大小(byte) | 范围 | 格式 | 描述 |
|---|---|---|---|---|
| date | 3 | 1000-01-01 至 9999-12-31 | YYYY-MM-DD | 日期值 |
| time | 3 | -838:59:59 至 838:59:59 | HH:MM:SS | 时间值或持续时间 |
| year | 1 | 1901 至 2155 | YYYY | 年份值 |
| datetime | 8 | 1000-01-01 00:00:00 至 9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值 |
| timestamp | 4 | 1970-01-01 00:00:01 至 2038-01-19 03:14:07 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值,时间戳 |
表结构
创建
1 | create table 表名( |
- 案例:创建user表
建表语句:
1 | create table tb_user ( |
我们往里面添加数据(我这里就使用DataGrip添加了,因为文中还没提及insert语句):
可以看见即使拥有相同的id,也没有出现错误,这与我们前面提到的id字段是一行数据的唯一标识,不能有重复值相违背。这是为什么呢?
其实我们现在创建表结构的时候, id这个字段我们只加了一个备注信息说明它是一个唯一标识,但是在数据库层面呢,并没有去限制字段存储的数据,所以id这个字段没有起到唯一标识的作用。
想要限制字段所存储的数据,就需要用到数据库中的约束。
这一点我们等会解析。
查询
1 | -- 查询当前数据库的所有表 !直接看 |
修改
- 添加字段
1 | -- 添加字段 |
- 修改字段
1 | -- 修改字段类型 |
1 | -- 修改字段名,字段类型 |
- 删除字段
1 | -- 删除字段 |
- 修改表名
1 | -- 修改表名 |
删除
1 | -- 删除表 |
- 在删除表时,表中的全部数据也会被删除
约束
概念:所谓约束就是作用在表中字段上的规则,用于限制存储在表中的数据
- 作用:就是来保证数据库当中数据的正确性、有效性和完整性。(后面的学习会验证这些)
在MySQL数据库当中,提供了以下5种约束:
| 约束 | 描述 | 关键字 |
|---|---|---|
| 非空约束 | 限制该字段值不能为null. | not null |
| 唯一约束 | 保证字段的所有数据都是唯一、不重复的. | unique |
| 主键约束 | 主键是一行数据的唯一标识,要求非空且唯一。(一般还是自增的) | primary key |
| 默认约束 | 保存数据时,如果未指定该字段值,则采用默认值。 | default |
| 外键约束 | 让两张表的数据建立连接,保证数据的一致性和完整性。 | foreign key |
- 注意:约束是作用于表中字段上的,可以在创建表/修改表的时候添加约束。
对于刚才的案例,添加如下约束
- id 是一行数据的 唯一标识
- username 用户名字段是非空且唯一的
- name 姓名字段是不允许存储空值的
- gender 性别字段是有默认值,默认为男
创建语句
1 | create table tb_user ( |
- 一个语句要多个约束,不同约束之间用空格隔开
在添加约束之后,可以看见我们刚刚创建的表就不能存在相同的id了。
DML
DML英文全称是Data Manipulation Language(数据操作语言),用来对数据库中表的数据记录进行增、删、改操作
- 添加数据(INSERT)
- 修改数据(UPDATE)
- 删除数据(DELETE)
增加
- 向指定字段添加数据
1 | insert into 表名 (字段名1, 字段名2) values (值1, 值2); |
- 全部字段添加数据
1 | insert into 表名 values (值1, 值2, ...); |
- 批量添加数据(指定字段)
1 | insert into 表名 (字段名1, 字段名2) values (值1, 值2), (值1, 值2); |
- 批量添加数据(全部字段)
1 | insert into 表名 values (值1, 值2, ...), (值1, 值2, ...); |
- 案例
- 案例1:向emp表的username, name, gender, phone, create_time, update_time字段插入数据
1 | -- 因为设计表时create_time, update_time两个字段不能为NULL,所以也做为要插入的字段 |
- 案例2:向temp表的所有字段插入数据
1 | -- 方式一 |
可以省去名
- 案例3:批量向emp表的username、name、gender字段插入数据
1 | insert into emp(username, name, gender, phone, create_time, update_time) |
- now()函数可以获得当前系统时间
修改
1 | update 表名 set 字段名1 = 值1 , 字段名2 = 值2 , .... [where 条件] ; |
- 案例1:将emp表中id为1的员工,姓名name字段更新为’张三’
1 | update emp set name='张三', update_time=now() where id=1; |
- 案例2:将emp表的所有员工入职日期更新为’2010-01-01’
1 | update emp set entry_date='2010-01-01', update_time=now(); |
- 必须带条件,不然默认为所有数据更改!
- 在修改数据时,一般需要同时修改公共字段update_time,将其修改为当前操作时间
删除
1 | delete from 表名 [where 条件] ; |
- 案例1:删除emp表中id为1的员工
1 | delete from emp where id = 1; |
- 案例2:删除emp表中所有员工
1 | delete from tb_emp; |
DQL
DQL英文全称是Data Query Language(数据查询语言),用来查询数据库表中的记录
- 查询关键字:SELECT
语法
1 | SELECT |
数据准备
1 | create table emp( |
基本查询
- 查询多个字段
1 | select 字段1, 字段2, 字段3 from 表名; //多个字段之间用逗号分隔 |
- 查询所有字段(通配符)
1 | select * from 表名; |
- 设置别名
1 | select 字段1 [ as 别名1 ] , 字段2 [ as 别名2 ] from 表名; |
- 去除重复记录
1 | select distinct 字段列表 from 表名; |
案例1:查询指定字段 name,entry_date并返回
1 | select name,entry_date from emp; |
案例2:查询返回所有字段
1 | select * from emp; |
*号代表查询所有字段,在实际开发中尽量少用(不直观、影响效率,具体原因在MySQL-从架构原理到索引及SQL优化中有讲)。
案例3:查询所有员工的 name, entry_date,并起别名(姓名、入职日期)
1 | -- 方式1: |
案例4:查询已有的员工关联了哪几种职位(不要重复)
1 | select distinct job from emp; |
如果不加distinct:
加了distinct之后:
distinct关键字就是用去除重复字段的。
条件查询(where)
1 | select 字段列表 from 表名 where 条件列表 ; -- 条件列表:意味着可以有多个条件 |
SQL语句当中构造条件的运算符分为两类:
- 比较运算符
- 逻辑运算符
常用的比较运算符如下:
| 比较运算符 | 功能 |
|---|---|
| > | 大于 |
| >= | 大于等于 |
| < | 小于 |
| <= | 小于等于 |
| = | 等于 |
| <> 或 != | 不等于 |
| between … and … | 在某个范围之内(含最小、最大值) |
| in(…) | 在in之后的列表中的值,多选一 |
| like 占位符 | 模糊匹配(_匹配单个字符, %匹配任意个字符) |
| is null | 是null |
常用的逻辑运算符如下:
| 逻辑运算符 | 功能 |
|---|---|
| and 或 && | 并且 (多个条件同时成立) |
| or 或 || | 或者 (多个条件任意一个成立) |
| not 或 ! | 非 , 不是 |
下面跟随示例来感受每个运算符的作用吧:
=
查询姓名为柴进的员工
1 | select * |
> <
查询薪资小于等于5000 的员工信息
1 | select * |
null
查询没有分配职位的员工信息
1 | select * |
注意:查询为NULL的数据时,不能使用 = null 或 !=null 。得使用 is null 或 is not null
查询有职位的员工信息
1 | select * |
!=和<>
查询密码不等于 ‘123456’ 的员工信息
1 | select * |
between…and..
- 查询入职日期 在 ‘2000-01-01’ (包含) 到 ‘2010-01-01’(包含) 之间的员工信息
1 | select * |
由此可见between,and包含端点
- 查询入职时间 在 ‘2000-01-01’ (包含) 到 ‘2010-01-01’(包含) 之间 且 性别为女 的员工信息
1 | select * |
两个条件中有两个and,可能混淆,可以用括号括住,使逻辑更加清晰
in
查询职位是 2 (讲师), 3 (学工主管), 4 (教研主管) 的员工信息
1 | select * |
1 | select * |
模糊查询
- 查询姓名为两个字的员工信息
1 | select * |
_只能表示一个字符
- 查询姓 ‘阮’ 的员工信息
1 | select * |
由此可见%关键字不会限制字数
- 查询姓名中包含 ‘二’ 的员工信息
1 | select * |
聚合函数
之前我们做的查询都是横向查询,就是根据条件一行一行的进行判断,而使用聚合函数查询就是纵向查询,它是对一列的值进行计算,然后返回一个结果值。
常用聚合函数:
| 函数 | 功能 |
|---|---|
| count | 按照列去统计有多少行数据,如果这一列中有null的行,该行不会被统计在其中 |
| max | 最大值 |
| min | 最小值 |
| avg | 平均值 |
| sum | 计算指定列的数值和,如果不是数值类型,那么计算结果为0 |
注意 : 聚合函数会忽略空值,对NULL值不作为统计。
count
统计该企业员工数量
1 | select count(id) from emp; |
1 | select count(job) from emp; |
由此可见聚合函数会忽略空值,对NULL值不作为统计
1 | select count(*) from emp; //优先 |
1 | select count(1) from emp; |
avg
统计该企业员工的平均薪资
1 | select avg(salary) from emp; |
min
统计该企业员工的最低薪资
1 | select min(salary) from emp; |
max
统计该企业员工的最高薪资
1 | select max(salary) from emp; |
sum
统计该企业每月要给员工发放的薪资总额(薪资之和)
1 | select sum(salary) from emp; |
分组查询(group by)
分组: 按照某一列或者某几列,把相同的数据进行合并输出
分组其实就是按列进行分类(指定列下相同的数据归为一类),然后可以对分类完的数据进行合并计算。
分组查询通常会使用聚合函数进行计算。
基本语法:
1 | select 字段列表 from 表名 [where 条件] group by 分组字段名 [having 分组后过滤条件]; |
- 根据性别分组 , 统计男性和女性员工的数量
1 | select gender, count(*) from emp group by gender; |
- 查询入职时间在 ‘2015-01-01’ (包含) 以前的员工 , 并对结果根据职位分组 , 获取员工数量大于等于2的职位
1 | select job, count(*) |
where与having的区别
行时机不同:where是分组之前进行过滤,不满足where条件,不参与分组;而having是分组之后对结果进行过滤。
**判断条件不同:**where不能对聚合函数进行判断,而having可以。
注意事项:
- 分组之后,查询的字段一般为聚合函数和分组字段,查询其他字段无任何意义。
- 执行顺序:where > 聚合函数 > having 。
如上,就是查询的字段错误的原因。
排序查询(order by)
1 | select 字段列表 |
排序方式:
- ASC :升序(默认值)可以不写
- DESC:降序
注意事项:如果是多字段排序,当第一个字段值相同时,才会根据第二个字段进行排序。
下面用一些具体示例来体会:
- 根据入职时间, 对员工进行升序排序
1 | select * |
删掉asc之后没有改变,证明升序排列是默认的。
- 根据入职时间,对员工进行降序排序
1 | select * |
- 根据入职时间对公司的员工进行升序排序,入职时间相同,再按照更新时间进行降序排序
1 | select * |
分页查询(limit)
基本语法:
1 | select 字段列表 from 表名 limit 起始索引, 查询记录数 ; |
注意事项:
- 起始索引从0开始;计算公式 :起始索引 = (查询页码 - 1)* 每页显示记录数
- 分页查询是数据库的方言,不同的数据库有不同的实现,MySQL中是LIMIT
- 如果查询的是第一页数据,起始索引可以省略,直接简写为 limit 条数
依旧是根据示例来理解:
- 起始索引0开始查询员工数据, 每页展示5条记录
1 | select * from emp limit 5; |
- 查询第1页员工数据, 每页展示5条记录
1 | select * from emp limit 0,5; |
第一页就是索引为0
- 查询第2页员工数据, 每页展示5条记录
1 | select * from emp limit 5,5; |
每页五条,第二页就是6-10,故起始索引为5
- 查询 第3页 员工数据, 每页展示5条记录
1 | select * from emp limit 10,5; |
总结:起始索引=(页码-1)每页展示记录数。