注:此博文为本人学习过程中的笔记
1.数据库约束
1.1.约束类型
not null - 指示某列不能存储 null 值
unique - 保证某列的每行必须有唯一的值
default - 规定没有给列赋值时的默认值
primary key - not null 和 unique 的结合。确保某列(或两个列多个列的结合)有唯一标识,有助于更容易更快速地找到表中的一个特定的记录
foreign key - 保证一个表中的数据匹配另一个表中的值的参照完整性
check - 保证列中的值符合特定条件。对于MySQL数据库,对check进行分析,但忽略check子句
1.2.not null约束
-- 重新设置学生表结构
drop table if exists student;
create table student(
id int not null,
sn int,
name varchar(20),
qq_mail varchar(20)
);
1.3.unique:唯一约束
指定sn列为唯一的,不重复的:
-- 重新设置学生表结构
drop table if exists student;
create table student(
id int not null,
sn int unique,
name varchar(20),
qq_mail varchar(20)
);
1.4.default:默认值约束
指定插入数据时,name列为空,默认值为unknow
-- 重新设置学生表结构
drop table if exists student;
create table student(
id int not null,
sn int unique,
name varchar(20) default 'unknow',
qq_mail varchar(20)
);
1.5.主键约束
指定id列为主键
-- 重新设置学生表结构
drop table if exists student;
create table student(
id int not null primary key,
sn int unique,
name varchar(20) default 'unknow',
qq_mail varchar(20)
);
对于整数类型的主键,常搭配自增长auto_increment来使用,插入数据对应字段不给值时,使用最大值+1,注意当我们插入数据失败时,自增的主键仍会+1,所以我们之后成功插入的数据可能是有跳跃的
-- 主键是not null和unique的结合,可以不用not null
id not primary key auto_increment,
1.6.foreign key:外键约束
外键用于关联其他表的主键或唯一值,语法:
foreign key (字段名) references 主表(列)
案例:
创建班级表classes,id为主键
-- 创建班级表,有使用MySQL关键字作为字段时,需要使用``来标识
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 primary key auto_increment,
sn int unique,
name varchar(20) default 'unknow',
qq_mail varchar(20),
classes_id int,
foreign key (classes_id) referencecs classes(id)
);
1.7.check约束(了解)
MySQL使用时不报错,但忽略该约束
drop table if exists test_user;
create table test_user(
id int,
name varchar(20),
sex varchar(1),
check (sex = '男' or sex = '女')
);
2.表的设计
2.1.三大范式
设计表的时候一般遵循三大范式,不过在实际生产中会根据需求来设计表,并不一定严格遵守
2.1.1.第一范式
·第一范式是关系型数据库的一个最基本的要求,不满足第一范式就不可以称为关系型数据库
·第一范式是指表里的字段不可再拆分(由于MySQL本身就是关系型数据库,所以MySQL里不存在能拆分的基本数据类型)
·在定义表的时候,对照到数据库中的数据类型
·每一个字段都可以用一个数据类型表示,那么当前这个表就天然满足第一范式
2.1.2.第二范式
·在满足第一范式的基本上,不存在非关键字段对任意候选键的部分函数依赖(存在于复合主键的情况下)
·名词解析:
非关键字段:可以理解为非主键字段
候选键:可以理解为主键,外键,没有主键时的唯一键
复合主键:一个表中不能存在多个主键,但是一个主键中可以包含多个列[primary key(列, 列)]
·当一个表中没有复合主键时,这个表天然满足第二范式
·当一个表中存在一些关键字依赖复合主键的一个部分,另一些关键字依赖复合主键的另一个部分,就说存在部分函数依赖
不满足第二范式可能会出现的问题
1.数据冗余
2.更新异常
3.插入异常
4.删除异常
2.1.3.第三范式
在第二范式的基础上,不存在非关键字字段,对任一候选键的传递依赖
2.2.设计表的三种关系
一对一
一对多
多对一
3.新增
插入查询结果
语法:
INSERT INTO table_name [column, column] SELELCT ...
案例:创建一张用户表,设计有name,email,sex,mobile。需要把已有的学生数据复制进来,可以复制的字段为name,qq_mail
-- 创建用户表
drop table if exists test_user;
create table test_user(
id int primary key auto_increment.
name varchar(20),
age int,
email varchar(20),
sex varchar(1),
mobile varchar(20)
);
-- 将学生表的所有数据赋值到用户表
insert into test_user(name, email) select name, qq_mail from student;
4.查询
4.1.聚合查询
4.1.1.聚合函数
常见的统计总数,计算平均值等操作,可以使用聚合函数来实现,常见的聚合函数有:
count() 返回查询到的数据的数量
sum() 返回查询的数据的总和,不是数字没有意义
avg() 返回查询到的数据的平均值,不是数字没有意义
max() 返回查询到的数据的最大值,不是数字没有意义
min() 返回查询到的数据的最小值,不是数字没有意义
案例:
count
-- 统计班级总共有多少同学
select count(*) from student;
select count(0) from student;
-- 统计班级收集的qq_mail有多少个,qq_mail为null的数据不会计入数据
select count(qq_mail) from student;
sum
-- 统计数学成绩总分
select sum(math) from exam_result;
-- 不及格<60的总分,没有结果,返回Null
select sum(math) from exam exam_result where math < 60;
avg
-- 统计平均总分
select avg(chinese + math + english) 平均总分 from exam_result;
max
-- 返回英语最高分
select max(english) from exam_result;
min
-- 返回>70分以上的数学最低分
select min(math) from exam_result where math > 70;
4.1.2.group by子句
select中使用group by子句可以对指定列进行分组查询。需要满足:使用group by进行分组查询时,select指定的字段必须时分组依据字段,其他字段若想出现在select中则必须包含在聚合函数中
select column1, sum(column2), ... from table group by column1, column3;
案例:职员表
create table emp(
id int primary key auto_increment,
name varchar(20) not null,
role varchar(20) not null,
salary numeric(11, 2)
);
insert into emp(name, role, salary) values
('马云','服务员', 1000.20),
('马化腾','游戏陪玩', 2000.99),
('孙悟空','游戏角色', 999.11),
('猪无能','游戏角色', 333.5),
('沙和尚','游戏角色', 700.33),
('隔壁老王','董事长', 12000.66);
查询每个角色的最高工资,最低工资和平均工资
select role, max(salary), min(salary), avg(salary) from emp group by role;
4.1.3.having
group by子句进行分组以后,需要对分组结果再进行过滤时,不能使用where语句,而需要用having
显示平均工资低于1500的角色和它的平均工资
select role, max(salary), min(salary), avg(salary) from emp group by role
having avg(salary) < 1500;
注:where 和 having 是可以混用的
4.2.联合查询
实际开发中往往数据来自不同的表,所以需要多表联合查询。多表查询是对多张表的数据取自笛卡尔积。(笛卡尔积就是几个表中的数据进行全排列)
4.2.1联合查询的步骤
1.首先确定哪几张表要参与查询
2.根据表与表之间的主外键关系确定过滤条件
3.精简查询字段,得到想要的结果
注:联合查询可以对关联表使用别名
案例:先初始化测试数据
insert into classes(name, `desc`) values
('计算机系2019级1班', '学习了计算机原理、C和Java语言、数据结构和算法'),
('中文系2019级3班','学习了中国传统文学'),
('自动化2019级5班','学习了机械自动化');
insert into student(sn, name, qq_mail, classes_id) values
('09982','黑旋风李逵','xuanfeng@qq.com',1),
('00835','菩提老祖',null,1),
('00391','白素贞',null,1),
('00031','许仙','xuxian@qq.com',1),
('00054','不想毕业',null,1),
('51234','好好说话','say@qq.com',2),
('83223','tellme',null,2),
('09527','老外学中文','foreigner@qq.com',2);
insert into course(name) values
('Java'),('中国传统文化'),('计算机原理'),('语文'),('高阶数学'),('英文');
insert into score(score, student_id, course_id) values
-- 黑旋风李逵
(70.5, 1, 1),(98.5, 1, 3),(33, 1, 5),(98, 1, 6),
-- 菩提老祖
(60, 2, 1),(59.5, 2, 5),
-- 白素贞
(33, 3, 1),(68, 3, 3),(99, 3, 5),
-- 许仙
(67, 4, 1),(23, 4, 3),(56, 4, 5),(72, 4, 6),
-- 不想毕业
(81, 5, 1),(37, 5, 5),
-- 好好说话
(56, 6, 2),(43, 6, 4),(79, 6, 6),
-- tellme
(80, 7, 2),(92, 7, 6);
4.2.2.内连接
语法:
select 字段 from 表1 别名1 join 表2 别名2 on 连接条件 and 其他条件;
select 字段 from 表1 别名1 join, 表2 别名2 where 连接条件 and 其他条件;
案例:
1.查询“许仙”同学的成绩
select sco.score from student stu 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 = '许仙';
2.查询所有同学的总成绩,及同学的个人信息
-- 成绩表对学生表是多对一关系,查询总成绩是根据成绩表的同学id进行分组的
select
stu.sn,
stu.name,
stu.qq_mail
sum(sco.score)
from
student stu
join score sco on stu.id = sco.student_id
group by
sco.student_id;
3.查询所有同学的成绩,及同学的个人信息
-- 查询出来的都是有成绩的同学,“老外学中文”同学没有显示
select * from student stu join score sco on stu.id = sco.student_id;
-- 学生表,成绩表,课程表3张表关联查询
select
stu.id,
stu.sn,
stu.qq_mail,
sco.score,
sco.course_id,
cou.name
from
student stu
join score sco on stu.id = sco.student_id
join course cou on sco.course_id = cou.id
group by
stu.id;
4.2.3.外连接
外连接分为左外连接和右外连接。如果联合查询,左侧的表完全显示我们就说是左外连接,右侧的表完全显示就是右外连接
-- 左外连接,表1完全显示
select 字段名 from 表名1 left join 表名2 on 连接条件;
-- 右外连接,表2完全显示
select 字段名 from 表名1 right join 表名2 on 连接条件;
案例:查询所有同学的成绩,及同学的个人信息,如果该同学没有成绩,也需要显
4.2.4.自连接
自连接是指在同一张表连接自身进行查询
案例:
显示所有“计算机原理”成绩比“Java”成绩高的成绩信息
-- 先查询“计算机原理”和“Java”课程的id
select id.name form course where name = 'Java' or name = '计算机原理';
-- 再查询成绩表中,“计算机原理”成绩比“Java”成绩好的信息
select
s1.*
from
score s1,
score s2
where
s1.student_id = s2.student_id
and s1.score < s2.score
and s1.course_id = 1;
and s2.course_id = 3;
-- 也可以使用join on语句来进行自连接查询
select
s1.*
from
score s1
join score s2 on s1.student_id = s2.student_id
and s1.score < s2.score
and s1.course_id = 1
and s2.course_id = 3;
以上查询只显示了成绩信息,并且是分布执行的,要显示学生及成绩信息,并在一条语句显示:
select
stu.*,
s1.score Java,
s2.score 计算机原理
from
score s1
join score s2 on s1.student_id = s2.student_id
join student stu on s1.student_id = stu.id
join course c1 on s1.course_id = c1.id
join course c2 on s2.course_id = c2.id
and s1.score < s2.score
and c1.name = 'Java'
and c2.name = '计算机原理';
4.2.5.子查询
子查询是指嵌入在其他sql语句中的select语句,也叫嵌套查询
单行子查询:
返回一行记录的子查询
查询与"不想毕业"同学的同班同学
select * from student where class_id = (select classes_id from student where name = '不想毕业');
多行子查询:
返回多行记录的子查询
案例:查询“语文”或“英语”的成绩信息
-- 使用in
select * from score where course_id in
(select id from course where name = '语文' or name = '英文');
-- 使用not in
select * from score where course_id not in
(select id from course where name != '语文' and name != '英文');
在from子句中使用子查询
子查询语句出现在from子句中,这里要用到数据查询的技巧,把一个子查询当作一个临时表使用
案例:查询所有比“中文系2019级3班”平均分高的成绩信息
-- 获取"中文系2019级3班的平均分,将其看作临时表"
select
avg(sco.score) score
join student stu on sco.student_id = stu.id
join classes cls on stu.classes_id = cls.id
where
cls.name = '中文系2019级3班';
查询成绩表中,比以上临时表平均分高的成绩:
select
*
from
score sco,
(
select
avg(sco.score) score
from
score sco
join student stu on sco.student_id = stu.id
join classes cls on stu.classes_id = cls.id
where
cls.name = '中文系2019级3班'
)tmp
where
sco.score > tmp.score;
4.2.6.合并查询
在实际应用中,为了合并多个select的执行结果,可以使用集合操作符union,union all。使用union和union all时,前后查询的结果集中,字段需要一致
union
该操作符用于取得两个结果集的并集。当使用该操作符时,会自动去掉结果集中的重复行
案例:查询id小于3,或者名字为“英文”的课程
select * from course where id < 3
union
select * from course where name = '英文';
-- 或者使用or来实现
select * from course where id < 3 or name = '英文';
union all
该操作符用于取得两个结果集的并集。当使用该操作时,不会去掉结果集中的重复行
案例:查询id小于3,或者名字为"Java"的课程
-- 可以看到结果集中重复数据Java
select * from course where id < 3
union all
select * from course where name = '英文';