1.数据库的约束
所谓的数据库约束就是,对于数据库的数据是有一定要求的。因为有些数据是合法数据,有些是非法数据。所以数据库约束就是自动得对数据的合法性进行校验检查得一系列机制,目的就是为了保证数据库中能够避免插入/修改一些非法得的数据。
1)NOT NULL
如下图所示,小编在创建这个表的时候是约束了id这一列是不能为null的,当我们尝试往这个表的两列中插入两个null,MySQL服务器就返回了一个报错信息。【Column 'id' cannot be null】
注:这里不仅仅是不可以插入null,修改也是一样,当我们要把某一行的id改成null也是会出现报错的;
2)UNIQUE
UNIQUE即唯一的,只能有一个【一列只能有一个;例如:id这一列不能出现相同的id值】
如上图所示,当我们插入第一个合法数据的时候,MySQL服务器提示小编已经成功插入这个合法的数据了;当小编插入第二个数据的时候,由于第二数据的name和第一个数据相同,所以MySQL服务器显示小编的数据插入失败;当小编换了一个策略的时候,即换成相同的id,但是名字不一样时,MySQL服务器还是显示小编插入数据失败;为什么第二次和第三次数据插入失败呢?其实我们从MySQL服务器返回的错误信息应该也能得知,是由于我们在创建这个表的时候使用了unique这个约束条件;
注:unique也是不仅仅是限制插入,同时也限制修改
3)DEFAULT
DEFAULT即为默认值,默认值在默认情况下是空。
4)PRIMARY KEY
PRIMARY KEY是我们这几个约束里面最重要的一个约束;
如上图所示,我们在创建这张表的时候,是设置id这列是受到PRIMARY KEY约束的,即唯一的、非空的。所以当小编插入第一行数据的时候是合法的,顺利插入;但是当小编插入第二行数据的时候由于id非唯一性,所以MySQL服务器拒绝了我的插入申请,并且返回了相关的错误信息;当小编不死心想要插入第三行数据的时候,由于id不满足非空性这个要求,导致小编的请求又再一次得遭受到MySQL服务器的拒绝;所以这个约束要求小编要插入的数据必须是唯一性的、非空性的;
注:一张表中只能有一个PRIMARY KEY【一张表里的记录,只能有一个作为身份标识的数据】;但是假设这个表里有多个PRIMARY KEY,那最终是以谁为准呢?答:虽然只能有一个主键,但是主键不一定只是一个列,也可以用多个列共同构成一个主键(联合主键)。既然这里的主键是唯一的,那有如何保障主键是唯一的?答:MySQL提供了一种”自增主键“这个机制。【自增主键:主键经常会使用int/bigint,而程序猿在插入数据的时候不必手动指定主键值,而是由数据库服务器自己给你分配一个主键【会从1开始,依次递增得分配主键的值】】。
create table student(id int primary key auto_increment,name varchar(20));
如上图所示,当我们创建出这么一个表的时候,在我们插入数据的时候就会自动生成主键。但是如下图所示,当我们删除掉其中一行数据数据的时候,MySQL服务器并不能顺利帮助我们修改这个id,这里的id发生了错乱。
注:此处的id的自动分配是有一定的局限性的。如果是单个MySQL服务器是没有问题的,但是如果是一个分布式系统,有多个MySQL服务器构成的集群,这时候依靠自增主键就不行了。【分布式系统:就是在面临数据量大时/客户请求量比较大时,这时一台服务器就搞不定,就需要多台机器处理这种情况】
面试题:如何保证不同数据库的id不重复?
答:这里可以利用分布式系统中生成唯一id的算法,但是这个实现这个算法有很多种方式
分布式唯一id = 时间戳 + 机房编号/主机编号 + 随机因子;
但是随机因子这里有一定概率生成相同的因子【但是概率比较小】;如果真发生了产生相同的随机因子,应该怎么应对?答:这种概率虽然在理论上时存在的,但是在工程中我们应该忽略这种不计;
5)FOREIGN KEY
FOREIGN KEY(外键):描述了两个表之间的关联关系;
create table student (id int primary key,name varchar(20),classId int,foreign key(classId) references class(classId));
如上述SQL语句,我们要描述好,当前表中的哪一列是被约束的列;是被哪个表中哪一列数据约束的;
如上图所示,在class这个表中,有100、101、102、103这几个classId,而我们也通过上述语句将其student这个表的classId与其关联起来。由图中的插入语句可以看出,当小编插入第一条数据的时候,因为classId为100,所以这条插入语句是合法的;但是当小编插入第二条语句的时候,因为classId是200,是不合法的,所以MySQL服务器拒绝了小编插入数据的请求;【注意:无论是修改还是插入数据都不可以离开建立表的时候的约束】
注:我们一般认为外键是父表约束子表,但是我们说父与子之间应当是”言传身教“的关系。例如,针对父表进行修改/删除操作的时候,如果当前倍修改/删除的值已经被子表引用,这样的操作也会失败;换一句说就是外键约束要保持,子表中的数据在对应的父表的列中要存在。【再问一个问题:如果我们不是删除其中一行记录,而是删除整个class表,那这个请求会被MySQL同意吗?答:不可以,删表其实就是在变相得删除记录,既然删记录不被允许,删表自然也是不被允许的。(就算子表为空,父表依旧是删不了的);唯一被允许的做法:先删除子表再删除父表】
如上图所示,子表是student,父表是class,外键就是classId这一列。从小编查询的信息可以看出,在子表中是100已经被使用了。所以当小编想删除class表中的classId为103的时候是可以顺利被删除的,而当小编斗胆尝试想要删除classId为100时,小编的请求就被MySQL服务器狠狠拒绝了。
问:如下图所示,当我们把上述进行测试的两个表进行删除,再重新创建class和student两个表。从下面的图中可以看出,class这个是可以顺利被创建的,但是当我们尝试创建student这个表时却被MySQL服务器拒绝了。那是什么原因导致的呢?
答:因为我们在创建这个父表的时候是没有主键的。所以我们在指定外键约束的时候要求父表中被关联的这一列的是主键或者unique。
场景题:大家肯定跟小编一样,经常在淘宝或者拼多多上买东西吧,当我们买到一个好用的东西,过了一段时间想要去复购的时候,我们就会去我们过往订单中去找这个商品,当我们找到这个订单的时候再点进去发现商品下架了,但是这个商品还依旧存在我们这个过往商品订单里。往往我们这个电商平台的数据库中一般会存在商品表和订单表这两个表的,由于这个两个表之间的goodsId应该要有约束的,所以这里就需要一个外键约束。但是在上面我们提到如果父表被子表引用是不可以删除父表中的相关记录的,必须要先删除子表中的记录再去删除父表中记录。对于这么一个情况,我们应该怎么去处理呢?
回答:简单的思路就是在数据库中把下架的商品隐藏起来,让用户查询的时候查不到,但是在这个数据库中这个商品还是存在,即这个商品在订单这个表中还是存在的并没有消失。【做法:给商品表中新增单独一列,表示商品是否在线。例如,值为1表示商品在线,值为0表示商品下架了】。上述删除,我们一般叫做”逻辑删除“;
2.表的设计
表的设计就是根据我们实际开发的场景需求,明确当前要创建几个表,每个表应该有哪些信息,这写表之间是不是有一定的联系,哪些表有联系,哪些信息有联系...
关键要点:
1.梳理清楚需求中的”实体“
2.再确定好实体之间的关系
实体:实际就是对象【需求中的关键性名词】
关系:一对一、一对多、多对多、没关系...【不同的关系对于设计表的方式是不同的】
关系
1)一对一
例如:一个学生只能拥有一个账号;一个账号也只能被一个学生拥有
student(studentId,name,accountId...)
account(accountId,username,password...)
student(studentId,name...)
account(accountId,username,password,studentId...)
student-account(studentId,name,username,password...)
上述两个表都是一对一的关系
2)一对多
例如:一个学生,只能在一个班级里面;一个班级,可以包含多个学生;
如下图所示,下面两种方式就是一对多的关系。但是由于MySQL不支持数组这样子的形式,所以对于下述的两种方式,我们主要是使用第二种形式去表示一对多的关系;
3)多对多
例如,一个学生,可以选择多门课程;一个课程也可以包含多个学生;
如下图所示,我们利用一个关联表来表示这种多对多的关系。从这个关联表可以看到,张三是选了语文和数学课,李四是选了语文课和英语课...
4)没关系
即表和表之间没有关系,相互独立不受任何表的约束。
3.查询操作的进阶
1)搭配插入使用的查询
所谓的搭配插入使用的查询就是把查询语句的查询结果作为插入的数值
insert into student2 select * from student;
如上图所示,刚开始student是有3条数据的,而student2是空的没有数据的。但是当我们通过上述语句,就可以把student中的数据填充的student2中,当我们再次查询的时候,发现student2中的数据同student中的一摸一样。
注:这里之所以插入成功就是因为student表和student2这两个表一样。【即要求查询出来的结果集合,列数/类型 要和插入的这个表匹配】
2)聚合查询
在小编的上一篇博客中提到了’表达式查询‘,所谓的表达式查询就是针对列和列之间的运算。而我们接下来即将学习的聚合查询就是相当于在行 与 行之间进行的运算的。
聚合函数
1.COUNT([DISTINCT] expr)
如上图所示,我们的exam_result这个表中一共有7行,我们通过执行select count(*) from exam_result;这个语句就可以得到这个表中总共有多少条数据。
注:这里我们要注意,当小编往exam_result这个表格插入一个全空的记录时,如果我们是全列查询,那查询出来的结果就是8;但是小编是指定列【name这个列】查询得到的结果就是7;
select count(math) from exam_result;
select count(distinct math) from exam_result;
上面两个语句所代表的含义是不一样的,所计算出来的结果也有可能不一样。如果math这一列有重复的数据,这两个语句计算出来的结果就是不一样的。但是不可以写(distinct *)。
※ 对于这个函数还需要注意的就是不能随便在括号前面加空格
2.SUM([DISTINCT] expr)
这个函数主要的作用:把一列的若干行,给进行求和(算数运算)【所以只能根据数字类型进行使用】;
如上图所示,是对表格中Chinese这一列进行总和运算【null不会被加进去】;
如上图所示,小编在前面有提到过是不能对字符串进行算数相加。但是小编偏偏不信邪,小编就斗胆来试一试,唉发现并没有报错,sum显示的是0,但是出现7个警告。小编这时候又冒出来了一个想法,新增了一个数据,让这个名字为”007“,结果再重复上述计算,sum就变成了7;
补充:sum还有别的用法,就是sum(表达式)
3.AVG([DISTINCT] expr)
4.MAX([DISTINCT] expr)
5.MIN([DISTINCT] expr)
GROUP BY子句
GROUP BY子句就是使用GROUP BY进行分组,再分别进行聚合查询。
针对指定的列进行分组,把这一列中,值相同的行,分成到一组中 → 得到若干个组 → 针对这些组,分别使用聚合函数。
如上图所示,我们先根据role这一行把这个7个人分成了3种职业,然后再对这三种职业的薪资求平均值。
※ 使用group 不要的时候还可以搭配条件,但是需要区分清楚该条件是分组之前的条件还是分组之后的条件
1.查询每个岗位的平均工资但是排除张三:
首先我们可以知道的是这个条件是分组之前的条件 →→ 直接使用where即可,where子句一般写在group by的前面
select role,avg(salary) from emp where name != '张三' group by role;
2.查询每个岗位的平均工资,但是排除平均工资超过两万的结果:
我们首先明确这个条件是在分组之后的 →→ 使用having描述条件【这个having实在ground by之后的】
select role,avg(salary) from emp group by role having avg(salary) < 20000;
3.查询每个岗位的平均薪资,不算张三,并且保留平均薪资 < 20000 的结果:
由上述的题目可以解读出这里既有分组前的条件也有分组后的条件 →→ 使用where+having结合
select role,avg(salary) from emp where name != '张三' group by role having avg(salary) < 20000;
3)联合查询/多表查询
所谓的联合查询的关键思路就是在于理解”笛卡尔积“工作过程【排列组合】,即笛卡尔积的列数是这两个表的列数相加,笛卡尔积的行数是这两个表的行数相乘。但是这个大表里面不一定所有的数据都是有意义【即并不是所有的数据都是符合客观事实的】,所以在我们进行多表查询的时候是需要把有意义的数据筛选出来,而无意义的数据过滤掉 →→ 找出连接条件【where 连接条件】
测试数据:
内连接
下面我们将使用一些案例来理解这个连接
1.查询“许仙”同学的 成绩:
由这个题目要求”许仙“和”成绩“可以知道我们要用到student和score这两个表进行联合查询,既然要进行联合查询
第一步就是要进行笛卡尔积操作
select * from student,score;
如上图所示,我们将student和score这两个表进行笛卡尔积之后汇总成了一个总表【由于这个表太大了,编没有截完】
第二步,将没有意义的数据删选出来【就需要找连接条件】
select * from student,score where student.id = score.student_id;
第三步,结合需求,进一步添加条件,针对结果进行筛选
select * from student,score where student.id = score.student_id and student.name = '许仙';
第四步,针对查询到的列进行精简,只保留需求中关心的列
select student.name,score.score from student,score where student.id = score.student_id and student.name = '许仙';
2.查询所有同学的总成绩:
注:按照题目中所说,我们需要查询所有得同学得总成绩。在我们前面学习的所有内容中,有表达式求和和聚合求和两种方式。而表达式求和是对于列与列时间进行查询,而在这里根据具体的题目要求我们是需要对某一位同学,即是要对行进行相加,所以这里对于总成绩的计算是利用聚合函数sum;
第一步,要先进行笛卡尔积
select * from student,score;
第二步,指定连接条件
select * from student,score where student.id = score.student_id;
第三步,精简一下列
select student.name,score.score from student,score where student.id = score.student_id;
第四步,针对上述结果,在进行groud by 聚合查询
select student.name,sum(score.score) from student,score where student.id = score.student_id group by name;
3.查询所有同学的成绩,及同学的个人信息:
这个跟上一个例子不同,这个是要列出每个同学每门课程的名字,和分数,所以这里就涉及到三张表,分别是学生表、课程表、分数表。
第一步,先算笛卡尔积
select * from student,score;
第二步,指定连接条件筛选数据
因为这里是涉及到三张表,即涉及到两个连接条件
select * from student,course,score where student.id = score.student_id and score.course_id = course.id;
第三步:精简列
select student.name,course.name,score.score from student,course,score where student.id = score.student_id and score.course_id = course.id;
外连接
小编已经在前面介绍过内连接了,那接下来小编要来介绍一下这个外连接了。那这个内连接和外连接有什么区别呢?如果两张表里面记录都是存在对应关系的,那内连接和外连接的结果都是一致的,如果存在不对应的记录,那内连接和外连接就会出现差别
如下图所示,是小编为了介绍外连接的用法而创建的测试数据。student这张表和score这张表明显是存在不对应的关系的。
第一步:先算笛卡尔积
select * from student,score;
第二步:指定连接条件筛选数据
select * from student,score where student.id = score.id;
如上图所示,就是我们在前面举的几个例子中的内连接
select * from student join score on student.id = score.id;
如上述所示,这也是内连接,这是内连接另外的一个方式
1)左外连接(left join)
select * from student left join score on student.id = score.id;
如上图所示,这个跟我们在内连接的时候得到结果不同,多了一个王五。而王五是在左侧表中中存在的,而右侧表中并没有王五这个信息。即左外连接就是以左侧表为基准,保证左侧表的每一个数据都会出现在最终的结果里。如果右侧表中不存在,对应的列就填成null;
2)右外连接(right join)
如上图所示,(解释是和左外连接是一样的,就是反过来而已)
自连接
所谓的自连接就是,一张表自己和自己进行笛卡尔积。【特殊情况:有的时候需要进行行与行之间的比较(主要是因为SQL只能进行列跟列之间的比较)即可以使用自连接把行关系转换为列关系】。下面我们来通过举一些例子来感受一下:
1.显示所有“计算机原理”成绩比“Java”成绩高的成绩信息::
通过上述这张表,再根据题目要求就是要求3要比1高
第一步,把score这个表自己跟自己进行”笛卡尔积“;
select * from score as s1,score as s2;
注意:这里进行自连接的语法和前面不一样,如果按照之前那样子进行”笛卡尔积“就会重现报错【同样名字的报错】,所以这里只能使用别名来进行自连接的”笛卡尔积“;
第二步:指定连接条件筛选数据
select * from score as s1,score as s2 where s1.student_id = s2.student_id;
如上图所示,第一列和第四列的分数就可以做一个比较了
第三步:再一次进行筛选
select * from score as s1,score as s2 where s1.student_id = s2.student_id and s1.course_id = 3 and s2.course_id = 1;
第四步:最后再进行一次分数比较的筛选
SELECT s1.student_id, s1.score, s2.score
FROM score AS s1, score AS s2
WHERE s1.student_id = s2.student_id
AND s1.course_id = 3
AND s2.course_id = 1
AND s1.score > s2.score;
子查询
子查询是指嵌入在其他sql语句中的select语句,也叫嵌套查询
1)单行子查询
单行子查询就是返回一行记录的子查询。下面小编将使用是一些例子来帮助大家理解这里面
查询与“不想毕业” 同学的同班同学:【这里的”不想毕业“是一个学生的名字】
如上图所示,从这个student表中,在name这一行可以看到”不想毕业“这位同学,而再次通过classes_id这一行便可以找到他的同班同学。
select classes_id from student where name = '不想毕业';
select name from student where classes_id = 1;
如上图所示,我们是先通过查询”不想毕业“这个人所在得到班级,然后再通过得到的班级查询在这个班级的所有人名字。
select name from student where classes_id = (select classes_id from student where name = '不想毕业');
如上图所示,上述的这个操作就是就是把我们在上一个操作中的两部SQL语句合并成一个SQL语句
2)多行子查询
多行子查询就是返回多行记录的子查询 → 一般使用 in 这个命令去处理
例如:查询“语文”或“英文”课程的成绩信息:
对于这个题目要求就会涉及到两张表,一张是课程表course,一张是成绩表score
第一种方法:使用联合查询
select * from course,score where course.id = score.course_id and (course.name = '语文' or course.name = '英文');
第二种方法:
1)先通过课程名字,找到课程id
2)在通过课程id在分数表中进行查询
select id from course where name = '语文' or name = '英文';
select score,student_id,score.course_id,score.score from score where score.course_id in (4,6);
如上图所示,这个是通过分两步SQL来进行查询的;
select score,student_id,score.course_id,score.score from score where score.course_id in (select id from course where name = '语文' or name = '英文');
如上图所示,就是通过套娃的方式,并且使用了 in 这个关键字完成题目的要求的。
合并查询
在实际应用中,为了合并多个select的执行结果,可以使用集合操作符 union,union all。使用UNION和UNION ALL时,前后查询的结果集中,字段需要一致。
1)union 关键字
该操作符用于取得两个结果集的并集。当使用该操作符时,会自动去掉结果集中的重复行。
例如:查询id小于3,或者名字为“英文”的课程
第一种方法:正常逻辑
第二种方法:使用union 关键字
※ 那小编在这里就要提出一个问题了,上面那个语句明显比下面这个语句简单啊,为什么我还要使用union 关键字呢?答:因为union 关键字允许把两个不同的表的查询结果合并到一起。【但是这里还是要要求合并两个SQL的结果集的列是需要匹配的】
2)union all
该操作符用于取得两个结果集的并集。当使用该操作符时,不会去掉结果集中的重复行。
例如:查询id小于3,或者名字为“Java”的课程
※ 不做多介绍了
目录
1.数据库的约束
1)NOT NULL
2)UNIQUE
3)DEFAULT
4)PRIMARY KEY
5)FOREIGN KEY
2.表的设计
关键要点:
关系
1)一对一
2)一对多
3)多对多
4)没关系
3.查询操作的进阶
1)搭配插入使用的查询
2)聚合查询
聚合函数
GROUP BY子句
3)联合查询/多表查询
内连接
外连接
1)左外连接(left join)
2)右外连接(right join)
自连接
子查询
1)单行子查询
2)多行子查询
合并查询
1)union 关键字
2)union all