目录
[1.2 not null约束](#1.2 not null约束)
[1.3 unique:唯一约束](#1.3 unique:唯一约束)
[1.4 default:默认约束](#1.4 default:默认约束)
[1.5 primary key:主键约束](#1.5 primary key:主键约束)
[1.6 foreign key:外键约束](#1.6 foreign key:外键约束)
[1.7 check约束(了解)](#1.7 check约束(了解))
[4.1 聚合查询](#4.1 聚合查询)
[4.1.1 聚合函数](#4.1.1 聚合函数)
[4.1.2 group by 子句](#4.1.2 group by 子句)
[4.1.3 having](#4.1.3 having)
[4.2 联合查询](#4.2 联合查询)
[4.2.2 外连接](#4.2.2 外连接)
[4.2.3 全外连接](#4.2.3 全外连接)
[4.2.4 自连接](#4.2.4 自连接)
[4.2.5 子查询](#4.2.5 子查询)
本篇博客的目标:
- 数据库约束关系
- 表的关系
- 新增
- 删除
- 修改
- 查询
1.数据库约束
1.1约束类型
- not null -指示某列不能存储null值
- unique-保证某列的每行必须有唯一的值
- default-规定没有给列赋值时的默认值
- primary key-not null和unique的结合。确保某列(或多个列的结合)有唯一标识,有助于更容易快速地找到表中的一个特定的记录。
- foreign key-保证一个表中的数据匹配另一个表中的值的参照完整性。
- check-保证列中的值符合指定的条件。对于mysql数据库,对check子句进行分析,但是忽略check子句。
1.2 not null约束
创建表时,可以指定某列不为空:
sql
-- 设置学生表结构
create table student(
id int not null,
sn int,
name varchar(20),
qq_mail varchar(20)
);
1.3 unique:唯一约束
指定sn列唯一、不重复的:
sql
create table student(
id int not null,
sn int unique,
name varchar(20),
qq_main varchar(20)
);
了解:unique约束,会让后续插入/修改数据的时候都先触发一次查询操作,通过这个查询,来确定当前的记录是否存在,即通过引入unique后执行效率会受到影响,就可能降低很多。
1.4 default:默认约束
指定插入数据时,name列为空,默认值unkown:
sql
create table student(
id int not null,
sn int unique,
name varchar(20) default 'unknow',
qq_mail varchar(20)
);
1.5 primary key:主键约束
指定id为主键:
sql
create table student(
id int not null primary key,
sn int unique,
name varchar(20) default 'unkown',
qq_main varchar(20)
);
主建的特点:具有唯一性;不允许为空。
++注意:一个表只能有一个主键(某些情况下可能会看到一个表用多个字段作为主键,即联合主键,这种方式本质上是将多个字段作为一个整体来作为一个主键来使用),但可以有多个唯一键;++
++主键可以作为外键,但是唯一键不可以(唯一键可能为空)。++
对于整数类型的主键,常搭配自增长++auto_increment++来使用。插入数据对应字段不给值时,使用最大值+1。
eg.
sql
-- 主键not null 和unique的结合,可以不用not null
id int primary key auto_increment,
1.6 foreign key:外键约束
外键用于关联其他表的主键或唯一键,语法:
sql
foreign key (字段名) reference 主表(列)
案例:
- 创建班级表classes,id作为主键:
sql
create table classes(
id int primary key auto_increment,
name varchar(20),
`desc` varchar(100)
);
-- 有使用mysql关键字作为字段时,需要使用` ` 反引号或" "来标识
- 创建学生表student,一个学生对应一个班级,一个班级对应多个学生。使用id为主键,classes_id为外键,关联班级表id
sql
create table student(
id int primary key auto_increment,
sn int unique,
name varchar(20) default 'unknown',
qq_mail varchar(20),
classes_id int,
foreign key (classes_id) references classes(id)
);
注意:
- ++一般使用父表的主键(主键或是唯一键)作为子表的外键。++
- ++插入数据时,必须插入父表,然后才能插入子表。++
- ++删除表时,必须先删除子表再删除父表。++
1.7 check约束(了解)
在实际开发中,可能会遇到这样一个场景,有一个age列,我们需要限制它的值为0~200,这样是为了防止输入的年龄值超过正常的范围。在mysql中我们可以使用check属性来为某一列添加条件检查。
语法:
sql
列名 数据类型 check(表达式)
eg.
sql
create table products(
id int,
name varchar(10),
type varchar(10),
city varchar(10) check(city in('广州','杭州')),
price decimal(5,1),
rdate date
);
city列中约束为check,该列的取值只能是"广州","杭州"。
2.表的设计
根据需求场景,明确实体,从而明确创建多少个表,此外,这些表中之间存在一定的联系。
实体可以认为是对象, 一般来说每个实体需要安排一个表,而表的列就对应实体的属性。
一对一:
一对多:
多对多:
- 创建课程表:
sql
create table course(
id int primary key auto_increment,
name varchar(20)
);
- 创建学生课程中间表,考试成绩表:
sql
create table score(
id int primary key auto_increment,
score decimal(3,1),
student_id int,
course_id int,
foreign key (student_id) references student(id),
foreign key (course_id) references course(id)
);
3.新增(进阶)
插入查询结果:
sql
insert into table_name [column] select...
案例:创建一张用户表,设计name姓名,email邮箱,sex性别,mobile手机号字段。需要把已有的学生数据复制进来,可以复制字段name。qq_emial
eg.
sql
create table test_uesr(
id int primary key auto_increment,
name varchar(20) comment '姓名',
age int comment '年龄',
email varchar(20) comment '邮箱',
sex varchar(1) comment '性别',
mobile varchar(20) comment '手机号'
);
-- 将学生表中的数据复制到用户表
insert into test_user(name,emial) select name,email from student;
4.查询
4.1 聚合查询
表达式查询是针对列和列之间的运算,聚合查询是在行和行之间的运算了。
4.1.1 聚合函数
常见的统计总数、计算平均值等操作,对行进行操作,可以使用聚合函数来实现,常见的聚合函数有:
案例:
- count
sql
-- 统计班级共有多少个同学
select count(*) from student;
select count(0) from student;
-- 统计班级收集的qq_mail 有多少个,qq_mail为null的数据不会计入结果
select count(qq_mail) from student;
- sum
sql
-- 统计数学成绩总分
select sum(math) from exam_result;
-- 不及格<60 的总分,没有结果,返回null
select sum(math) from exam_result where math<60;
- avg
sql
-- 统计平均分
select avg(chinese +math+english) 平均总分 from exam_result;
- max
sql
-- 返回英语最高分
select max(english) from exam_result;
- min
sql
-- 返回>70分以上的数学最高分
select min(math) from exam_result where math>70;
4.1.2 group by 子句
select 中使用group by子句可以对指定列进行分组查询。需要满足:使用group by进行分组查询时,++select指定的字段必须是"分组依据字段",字段中相同的为一组,其他字段若想出现在select中则必须包含在聚合函数中。++
语法:
sql
select column,sum(column2),...from table group by column1,column3;
案例:
- 准备测试表及数据:职员表,有id(主键),name(姓名),role(角色),salary(薪水)
sql
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);
- 查询每个角色的最高工资,最低工资和平均工资
sql
select role,max(salary),min(salary),avg(salary) from emp group by role;
++我们一说起group by子句,就应该把它和聚合函数结合起来。++
4.1.3 having
group by 子句进行分组后,需要对分组结果在进行条件过滤时,不能使用where语句,而需要使用having。
在进行查询的时候,要区分条件时分组之前的条件还是分组之后的条件。
eg.
1)查询每个岗位的平均工资,但是排除张三
sql
select role,avg(salary) from emp where name!='张三' group by role;
2)查询每个岗位的平均薪资,但是排除平均薪资超过2w的结果。
sql
select role,avg(salary) from emp group by having avg(salary)<20000;
注意:
having子句不能单独使用,而必须结合group by 子句一起使用。并且having子句必须写在group by子句之后。
子句使用顺序
顺序:
select-->from-->where-->group by-->having-->order by-->limit
4.2 联合查询
实际开发中往往数据来自不同的表,所以需要多联合查询。多表查询是对多张表的数据取笛卡尔积:
笛卡尔积是通过排列组合的方式得到的一个更大的表
笛卡尔积的列数,是这两个表的列数相加。
笛卡尔积的行数,是这两个表的行数相乘。
注意:关联查询可以对关联表使用别名。
初始化测试数据:
sql
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.1内连接
语法:
sql
select 字段 from 表1 别名 [inner] join 表2 别名2 on 连接条件 and 其他条件;
select 字段 from 表1 别名,表2 别名2 where 连接条件 and 其他条件;
案例:
(1)查询'许仙'同学的成绩
sql
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 ='许仙';
(2) 查询所有同学的总成绩及同学的个人信息
sql
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) ch查询所有同学的成绩,及同学的个人信息:
sql
-- 查询出来的都是有成绩的同学,"老外学中文"同学没有显示
select *from student stu join score sco on stu.id =sco.student_id;
-- 学生表、成绩表。课程表3张表关联查询
select
stu.id,
stu.sn,
stu.name,
stu.qq_mail,
sco.score,
sco.course,
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
order by
stu.id;
4.2.2 外连接
外连接分为左外连接和右外连接。如果联合查询,左侧的表完全显示我们就说是左外连接;右侧的表完全显示我们就说是右外连接;如果这两个表的记录存在对应关系,内连接和外连接的结果是一致的,否则不一致。
语法:
sql
-- 左外连接,表1完全显示
select 字段名 from 表名 left join 表名2 on 连接条件;
-- 右外连接,表2完全显示
select 字段 from 表1 right join 表名2 on 连接条件;
案例:查询所有同学的成绩及同学的个人信息,如果该同学没有成绩,也需要显示
sql
-- 左连接
select *from student stu left join score sco on stu.id = sco.student_id;
-- 右连接
select *from score sco right join student stu on stu.id =sco.student_id;
-- 学生表、成绩表、课程表3张表关联查询
SELECT
stu.id,
stu.sn,
stu.NAME,
stu.qq_mail,
sco.score,
sco.course_id,
cou.NAME
FROM
student stu
LEFT JOIN score sco ON stu.id = sco.student_id
LEFT JOIN course cou ON sco.course_id = cou.id
ORDER BY
stu.id;
4.2.3 全外连接
mysql并没有提供full outer join 这样的连接方式,如果想要在mysql中实现全外连接,我们可以稍微变通一下:首先获取左外连接的结果,然后在获取右外连接的结果,最后使用union求并集即可。
eg.
sql
select *from staff left join market on staff.sid = market.sid
union
select *from staff right outer join market on staff.sid = market.sid;
图示了解这几个连接:
4.2.4 自连接
自连接是指在同一张表连接自身进行查询。
语法:
sql
select 列名
from 表名1 as 别名1,表名2 as 别名2;
案例:
显示所有"计算机原理"成绩比"Java"成绩高的成绩信息
sql
-- 先查询"计算机原理"和"Java"课程的id
select id,name from 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;
以上查询只显示了成绩信息,并且是分布式执行的。要显示学生及成绩信息,并在一条语句显示:
sql
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语句,也叫嵌套查询。
- 单行子查询:返回一行记录的子查询
查询与"不想毕业"同学的同班同学:
sql
select *from student where classes_id =(select classes_id from student where
name = '不想毕业');
- 多行子查询:返回多行记录的子查询
案例:查询"语文"或"英文课程"的成绩信息
1.[not] in 关键字:
sql
-- 使用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!='英文');
2.[not] exists 关键字:
sql
--使用 exists
select *from score sco where exists (select sco.id from course cou where (name ='语文'
or name ='英文') and cou.id =sco.course_id);
-- 使用not exists
select *from score sco where not exists (select sco.id from course cou where
(name!='语文' and name !='英文') and cou.id = sco.course_id);
sql
-- 获取"中文系2019级3班"的平均分,将其看做临时表(参照表)
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班';
查询成绩表中,比以上临时表平均分高的成绩:
sql
-- 先得到平均分,然后在找比平均分高的成绩
select
*
from
score sco,
(
select
avg(sco.score) avg_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.avg_score;
4.2.5合并查询
在实际应用中,为了合并多个select的执行结果,可以使用集合操作符union,union all,使用union和union all时。前后查询的结果集中,字段个数和类型需要一致(列名不需要一致)。
- union
该操作符用于取得两个结果集的并集。当使用该操作符时,会自动去掉结果集中的重复行。
案例:查询id小于3,或者名字为"英文"课程:
sql
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"的课程
sql
-- 可以看到结果集中出现重复数据Java
select *from course where id<3
union all
select *from course where name='java';
5.内容总结
- 数据库约束
- 表的关系
- 一对一:
- 一对多:
- 多对多:需要创建中间表来映射两张表的关系
- 新增(进阶):
sql
-- 将一个表的数据复制到另一个表中去
insert into table_name ([column1],[column2]....) select....
- 查询
1.聚合函数:max,min,avg,count,sum
2.分组查询:group by....having....
3.内连接:
sql
select....from 表1 ,表2 where 条件
-- inner可以缺省
select...from 表1 join 表2 on where 其他条件
4.外连接:
sql
select...from 表1 left/right join 表2 on 条件 where 其他条件
5.自连接:
sql
select ... from 表1,表1 where 条件
select ... from 表1 join 表1 on 条件
6.子查询:
sql
-- 单行子查询
select...from 表1 where 字段1=(select...from...);
--[not] in
select..from 表1 where 字段1 in (select...from...);
-- [not] exists
select...from 表1 where exists(select...from...where 条件);
-- 临时表:from子句中的子查询
select...from 表1,(select..from...) as tmp where 条件
7.合并查询:
sql
-- union 去除重复数据
select..from..where 条件
union
select..from..where 条件
-- union all 不去重
select..from...where 条件
union all
select...from...where 条件
-- 使用union和union all 时,前后查询的结果集中,字段需要一致
关键字执行顺序:
SQL查询中各个关键字的执行先后顺序:from>on>join>where>group by>with>having>select>distinct>order by>limit