MySQL数据库基础知识总结
什么是数据库?
数据库是用来组织、存储和管理数据的仓库。
数据库是一种软件,组织大量的数据(一般在磁盘上),应用到数据结构。
常见的数据库有MySQL、Oracle、SQL Sever等。在这里我们只讨论MySQL。
文章目录
一、数据库操作
1.1 显示当前数据库
-- 显示当前数据库
show databases;
注意:
(1)MySQL中的sql语句绝大部分都需要带分号,个别不需要。
(2)sql中不区分大小写。
(3)可以使用其他编辑器来输入sql,这时候是有高亮和补全的,例如:VSCode。
(4)在数据库中,-- 后面是注释内容,注意之间要有一个空格。
1.2 创建数据库
-- 注意:数据库名只能由数字、字母、下划线组成,不能有其他的。
create database 数据库名;
create database 数据库名 character set utf8mb4;
-- 搭配if not exists
create database if not exists 数据库名;
说明:
当我们创建数据库没有指定字符集和校验规则时,系统使用默认字符集:utf8,校验规则是:utf8_general_ci。MySQL的utf8编码不是真正的utf8,没有包含某些复杂的中文字符。MySQL真正的utf8是使用utf8mb4,建议大家都使用utf8mb4。
1.3 使用数据库
use 数据库名;-- 使用、指定数据库
1.4 删除数据库
drop database 数据库名;
-- 搭配if exists,如果存在则删除
drop database if exists 数据库名;
注意:删除操作非常危险!!工作中一定要注意!一旦删除,通过常规手段时无法找回的。
二、常用数据类型
有了数据库之后,要真正去存储组织数据,还需要数据表。创建表的时候就需要指定表的结构。表结构中就涵盖了有哪些字段,以及每个字段的类型是什么。
2.1 数值类型
一般不建议使用float(m,d)和double(m,d),会发生精度丢失,误差比较大,建议使用decimal(m,d)。
注意:
- 1字节(bytes)= 8bit。
- 数值类型可以指定为无符号(unsigned),表示不取负数。尽量不使用unsigned,对于int类型可能存放不下的数据,int unsigned同样可能存放不下,与其如此,还不如设计时,将int类型提升为bigint类型。
2.2 字符串类型
注意:
字符串都需要使用单引号引起来,如 ‘字符串内容’ 。
2.3 日期类型
小结:
常用数据类型:
int
double(m,d)
decimal(m,d) :m指定长度,d指定小数位数。表示更精确的小数.
varchar
text
在MySQL中没有数组类型
三、表的操作
表操作之前,必须要先use数据库
3.1 创建表
-- 先指定数据库
use 数据库名;
create table [if not exists] 表名(
字段1 数据类型 字段属性,
字段2 数据类型 字段属性,
......
字段n 数据类型 字段属性
-- 注意,最后一个字段定义时不加逗号
);
可以使用comment增加字段说明,在表结构中可以看见,如下:
-- 举例:
create table stu_test(
id int,
name varchar(20) comment '姓名',
password varchar(50) comment '密码',
age int,
sex varchar(1),
birthday timestamp,
amout decimal (13,2),
resume text
);
注意:
创建表的时候,如果表名或者列名和sql中的关键字重复了,就会报错。
解决方法:使用反引号将名字冲突的部分引起来。(键盘esc的下方)
-- 例如:
create table `create`(
id int,
name varchar(20)
);
3.2 删除表
drop table 表名;
-- 搭配 if exists,如果存在某表就删除
drop table if exists 表名;
注意:慎之又慎!!
3.3 查看表结构
desc 表名;-- 查看表结构
3.4 查看当前所有表
show tables;-- 查看当前所有表
四、MySQL表的增删改查(CRUD)
CRUD 即增加(Create)、查询(Retrieve)、更新(Update)、删除(Delete)四个单词的首字母缩写。
4.1 新增数据
4.1.1 全列插入
insert into 表名 values (对应的字段内容);
-- 注意:插入的字段的数目和类型需要和表结构中的要求一致。
整数可以直接插入,字符串需要带引号,对单双引号无要求。
-- 举例:
insert into student values(1,'test',98.5,'test@qq.com');
4.1.2 指定列插入
根据需要只插入其中的某几列
insert into 表名 (若干个指定列) values (对应的列数据);
注意:
(1)数目和类型都要对应;
(2)指定的列都得在表头中存在;
(3)没有被插入的列会默认被填充为null
-- 举例:
insert into student (id,name) values (1,'孙悟空');
4.1.3 一次插入多条记录
插入的时候可以一个insert语句同时插入多组数据
insert into 表名 values (对应的列数据),(对应的列数据),(对应的列数据)......
例如:
-- 举例:
insert into student (id,name) values (1,'孙悟空'),(2,'猪八戒'),(3,'沙和尚'),(4,'唐僧');
4.2 查询数据
SQL中最复杂的就是查找,有各种各样的查找方法。
4.2.1 全列查找
*是一个通配符,意思是把所有的列都查找出来。
-- 通常情况下不建议使用
select * from 表名;
-- 1.查询的列越多,意味着需要传输的数据量越大;
-- 2.可能会影响到索引的使用。(索引待后面课程讲解)
注意:
这样的查找方式仅限于在测试环境中使用,千万不能在生产环境的服务器上执行这样的SQL!!
拓展
:生产环境的服务器压力本来就很大,生产环境的服务器(数据库)数据量非常多,一旦执行这样的select*操作,可能就会给生产环境的服务器造成很大负担,甚至导致服务器卡死或者宕机。生产环境的服务器如果坏了,会给用户造成恶劣影响,很可能给公司带来很大的经济损失。
在工作中,操作生产环境时,一定要非常非常谨慎!!!
4.2.2 指定列查找
select 列名 from 表名;
4.2.3 查询字段为表达式
针对查到的列进行一定的表达式计算。
select 表达式 from 表名;
4.2.4 查询字段指定别名
select 表达式 (as) 别名 from 表名;
4.2.5 指定列去重
使用 distinct 关键字
select distinct 指定列 from 表名;
4.2.6 排序
asc 表示升序,从小到大(一般可以省略,默认升序)
desc 表示降序,从大到小,需要指定。
select 列名 from 表名 order by 列/表达式 asc/desc;
4.2.7 条件查询
使用 where 进行条件查询,搭配比较运算符和逻辑运算符。
select 列名 from 表名 where 条件表达式;
-- 条件为真的记录才会被最终获取到
注意:
1. where条件可以使用表达式,但不能使用别名。
2. and 的优先级高于or,在同时使用时,需要使用小括号() 包裹优先执行部分。
3. 判断是否和null相等时,要用 <=> 或者 is null,不能直接用 = 。
4. 在MySQL中如果对null进行比较、求和等操作时,结果的值也都是null,一般工作中的数据库很少会涉及null。
4.2.8 分页查询
select 列名 from 表名 limit 结果的数目;
select 列名 from 表名 limit 结果的数目 offset 起始记录的下标 ;
当写一个查询语句时,有多个条件共同控制过滤数据,应该把能过滤掉的记录多的条件写在前面。
4.3 修改数据
update 表名 set 列名 = 新的值 where 筛选条件;
- 可以同时指定修改多个列;
- 修改时也可以指定一些更复杂的表达式,如score = score+10;
- 修改数据时,数据不应该超出指定列的数据类型的范围。
4.4 删除数据
delete from 表名 where 筛选条件;
拓展
:
数据库的数据都是存到磁盘上的,如果数据特别多,超出磁盘容纳的上限,此时就无法继续写入数据了。根据数据的重要情况,决定是定期清理还是定期备份。还可以使用分布式的方法,针对数据库进行分库、分表,使用多台机器共同保存所有的数据集合。
删除操作非常危险,一旦数据被删除了,通过常规手段是无法恢复的,数据库都会支持一些丰富的“权限”控制。
例如:有些数据,只能读,不能改。
有些数据,能读能改,不能删除。
五、数据库约束
约束:数据库针对数据进行一系列的校验。如果发现插入的数据不符合约束中描述的校验规则,就会插入失败,为了更好的保证数据的正确性。
5.1 约束类型
NOT NULL
: 指示某列不为空(null)。
UNIQUE
:保证某列的所有行数据是不能重复的。
DEFAULT
: 规定没有给列赋值时的默认值。
PRIMARY KEY (主键)
: NOT NULL 和 UNIQUE 的结合。确保某列(或两个列多个列的结合)有唯一标识,有助于更容易更快速地找到表中的一个特定的记录。
可以和 auto_increment 搭配,表示自增。如果表中没有任何记录,自增从1开始,否则,从上一条记录往下自增。
【注意】:
(1)如果把中间的某个数据删除之后,再次插入其他的数据,刚才删掉的自增主键的值不会被重复利用!
(2)自增要保证在有效范围内,比如int类型的自增主键,有效范围就是-21亿~+21亿,如果数据超出范围,继续自增就会出现溢出的情况。
FOREIGN KEY(外键)
:保证一个表中的数据匹配另一个表中的值的参照完整性,描述两张表之间的关联关系。
外键用于关联其他表的主键或唯一键。
格式:
foreign key (字段名) references 主表(列)
需要指定三方面关系:
(1)指定和当前表中的哪列进行关联;
(2)指定和哪张表关联;
(3)指定和目标表中的哪一列关联。
指定后,后续插入数据时,MySQL会自动检查当前字段的值是否在另一个表的列中出现过,没有的话就会插入失败。
使用外键,会对插入操作的效率产生一定的影响,外键约束也会影响表的删除。
CHECK
: 保证列中的值符合指定的条件。对于MySQL数据库,对CHECK子句进行分析,但是忽略CHECK子句。(这里不详细讲解)
举例:创建classes班级表,id 为主键:
drop table if exists classes;
create table classes (
id int primary key auto_increment,
name varchar(20),
`desc` varchar(100)-- 字段和关键字同名,使用反引号``来标识。
);
创建学生表student,一个学生对应一个班级,一个班级对应多个学生。使用id作为主键,classes_id 作为外键,关联班级表id :
drop table if exists student;
create table student (
-- id int not null, 使用主键就可以省略not null
id int primary key auto_increment,-- 主键约束
-- 主键时not null 和 unique 的结合
sn int unique,
name varchar(20) default 'unknown',-- 设置默认值
qq_mail varchar(20),
classes_id int,
foreign key (classes_id) references classes(id)-- 指定外键
);
六、表的设计
聚焦于设计表时的实体间的关系。
三大范式:一对一、一对多、多对多。
一对一关系:一个人只能有一个身份证,一个身份证只能被一个人拥有。
一对多关系:一个学生只能在一个班级,一个班级可以有多个学生。
一对多关系,需要通过外键来组织。
比如:
注意
:不可以当成是一个数组,数组中的每一个元素表示一个学生信息。MySQL中没有数组类型。
多对多关系:一个学生可以有多门课程,一门课程也可以被多个学生选择。
多对多关系很复杂,需要引入中间表来解决这个问题。
七、查询
7.1 聚合查询
把查询结果中的若干行数据合并到一起,一般需要搭配MySQL中的一些内置“函数”。
7.1.1 聚合函数
常见的聚合函数有:
count:计算结果的行数,不包含空行。
count([distinct] expr) -- 返回查询的数据的数量(非null)
-- 注意:count和(之间没有空格,否则会被当成列名!
sum:返回查询到的数据的总和,不是数字没有意义。
sum([distinct] expr)
avg:返回查询到的数据的平均值,不是数字没有意义。
avg([distinct] expr)
max:返回查询到的数据的最大值,不是数字没有意义。
max([distinct] expr)
min:返回查询到的数据的最小值,不是数字没有意义。
min([distinct] expr)
7.2 分组查询
7.2.1 group by 子句
select 中使用group by子句可以对指定列进行分组查询,类似于hadoop中的map reduce操作中的reduce。
需要满足
:使用 group by进行分组查询时,select 指定的字段必须是“分组依据字段”,其他字段若想出现在select 中则必须包含在聚合函数中。
select 列1,sum(列2),...from 表名 group by 列1,列2;
-- 举例:查询每个角色的最高工资、最低工资和平均工资
select role,max(salary),min(salary),avg(salary) from emp group by role;
7.2.2 having
使用group by子句进行分组后,需要对分组结果再进行条件过滤时,不能使用where语句,而需要用having。
-- 显示平均工资低于1500的角色和它的平均工资
select role,max(salary),min(salary),avg(salary) from emp group by role having avg(salary)<1500;
小结:having与group by的区别:
having是针对group by 之后的结果进行筛选,where是针对原始表中的每条记录都进行筛选。
7.3 联合查询
这部分在MySQL中非常重要也非常难理解,多多练习!
在实际开发中,数据往往来自不同的表,所以需要多表联合查询。多表查询是对多张表的数据取笛卡尔积。
【注意】:关联查询可以对关联表使用别名。
7.3.1 内连接
内连接也称为等值连接,返回两张表都满足条件的部分。inner join 等价于 join。
-- 语法:
select 字段 from 表A 别名A [inner] join 表B 别名B on 连接条件 and 其他条件;
select 字段 from 表A 别名A,表B 别名B where 连接条件 and 其他条件;
举例:
--查询许仙同学的成绩
select sco.score from student stu inner join score sco on stu.id=sco.student_id
and stu.name='许仙';
-- 或者
select sco.score from student stu, score sco where stu.id=sco.student_id and
stu.name='许仙';
7.3.2 外连接
外连接分为左外连接(left join)和右外连接(right join)。
如果联合查询,左侧的表完全显示我们就说是左外连接;右侧的表完全显示我们就说是右外连接。
-- 左外连接,表A完全显示
-- 取左边的表的全部,右边的表按条件,符合的显示,不符合则显示null
select 字段名 from 表名A left join 表名B on 连接条件;
-- 右外连接,表B完全显示
-- 取右边的表的全部,左边的表按条件,符合的显示,不符合则显示null
select 字段名 from 表名A right join 表名B on 连接条件;
7.3.3 自连接
自连接是指在同一张表连接自身进行查询。本质上相当于同一列中的两行记录转换成不同列的同一行记录。
-- 找出所有计算机原理比Java成绩高的同学id
-- (1)先找到Java和计算机原理的课程id
select * from course where name = 'java';
select * from course where name = '计算机原理';
-- (2)按照课程id在分数表中筛选数据
select
-- (a)针对score表自身进行笛卡尔积
s1.student_id,s1.score,s1.course_id,s2.student_id,s2.score,s2.course_id
from
score s1,
score s2
where
--(b)加上学生id的限制
s1.student_id = s2.student_id
-- (c)加上课程id的限制
and s1.course_id = 3
and s2.course_id = 1
-- (d)按照分数大小进行比较
and s1.score > s2.score;
7.3.4 子查询
子查询是指嵌入在其他sql语句中的select语句,也叫嵌套查询。
分为单行子查询和多行子查询。
单行子查询:返回一行记录的子查询。
-- 举例:查询与“不想毕业” 同学的同班同学
select * from student where classes_id=(select classes_id from student where
name='不想毕业');
多行子查询:返回多行记录的子查询。
(1)借助 in 的方式来查询
先执行子查询,把子查询的结果保存到内存中,再进行主查询,再结合刚才子查询的结果来筛选最终结果。
注意
:多行子查询时要使用 in 而不是 = 。
-- 举例:查询“语文“或者”英文“课程对应的成绩
select * from score where course_id in (select id from course where name = '语文' or name = '英文' );
(2)借助exists方式来查询
先执行主查询,再触发子查询。
-- 举例:查询“语文“或者”英文“课程对应的成绩
select * from score where exists (select score.course_id from course where (name = '语文' or name = '英文') and course_id = score.course_id);
针对主表中的每个查询记录,都会再触发一次子查询。执行很多次,相当于用时间来换空间。
注意
:
如果子表查询的结果集合比较小,就使用in (更推荐使用);如果子表查询的结果集合比较大,而主表的集合小,就使用exists。
7.3.5 合并查询
在实际应用中,为了合并多个select的执行结果,可以使用集合操作符 union,union all。使用union
和union all 时,前后查询的结果集中,字段需要一致。
union
:该操作符用于取得两个结果集的并集。当使用该操作符时,会自动去掉结果集中的重复行。
union all
:该操作符用于取得两个结果集的并集。当使用该操作符时,不会去掉结果集中的重复行。
-- 举例:查询id < 3 并且名字为 '英文'的课程。
-- 普通查询
select * from course where id <3 or name = '英文';
-- 使用union 关键字
select * from course where id < 3 union select * from course where name = '英文';
-- 如果两个查询结果中存在相同的记录,就会只保留一个
-- 如果不想去重,可以使用union all即可。
拓展
:SQL的特点,不需要用户来指定一步一步该如何执行,只要告诉数据库,最终想要啥数据即可。像C/Java这种编程语言则需要告诉计算机每一步都该如何执行。
小结
: MySQL基础知识整体都比较简单,联合查询部分略有难度,需要多加练习。推荐刷牛客题库里的SQL必知必会。面试常考的索引和事务部分后续会讲解。