MySQL 约束&特殊查询
文章目录
- [MySQL 约束&特殊查询](#MySQL 约束&特殊查询)
-
- [1. 数据库约束](#1. 数据库约束)
-
- [1.1 约束类型](#1.1 约束类型)
- [1.2 NULL约束](#1.2 NULL约束)
- [1.3 NUIQUE:唯一约束](#1.3 NUIQUE:唯一约束)
- [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约束)
- [2. 表的关系](#2. 表的关系)
-
- [2.1 一对一](#2.1 一对一)
- [2.2 一对多](#2.2 一对多)
- [2.3 多对多](#2.3 多对多)
- [3. 特殊新增](#3. 特殊新增)
- [4. 查询](#4. 查询)
-
- [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.1 初始化](#4.2.1 初始化)
- [4.2.2 操作步骤](#4.2.2 操作步骤)
- [4.2.3 内连接](#4.2.3 内连接)
- [4.2.4 外连接](#4.2.4 外连接)
- [4.2.5 自连接](#4.2.5 自连接)
- [4.2.6 合并查询](#4.2.6 合并查询)
1. 数据库约束
1.1 约束类型
NOT NULL: 指示某列不能存储NULL值
UNIQUE:保证某列的每行必须有唯一的值
DEFAULT:规定没有给列赋值时的默认值
PRIMARY KEY:确保某列(或两个列多个列的结合)有唯一标识,有助于更容易更快速地找到表中的一个特定的记录
FOREIGN KEY:保证一个表中的数据匹配另一个表中的值的参照完整性
CHECK:保证列中的值符合指定的条件。对于MySQL数据库,对CHECK子句进行分析,但是忽略CHECK子句
1.2 NULL约束
创建表时,可以指定某列不为空:
sql
-- 设置学生表结构
CREATE TABLE student_5 (
id INT NOT NULL,
sn INT,
name VARCHAR(20),
qq_mail VARCHAR(20)
);
1.3 NUIQUE:唯一约束
指定sn列中的值唯一且不重复的:
sql
-- 重新设置学生表结构
DROP TABLE IF EXISTS student_5;
CREATE TABLE student (
id INT NOT NULL,
sn INT UNIQUE,
name VARCHAR(20),
qq_mail VARCHAR(20)
);
1.4 DEFAULT:默认值约束
指定插入数据时,name列为空,默认值为unknow
sql
-- 重新设置学生表结构
DROP TABLE IF EXISTS student_5;
CREATE TABLE student (
id INT NOT NULL,
sn INT UNIQUE,
name VARCHAR(20) DEFAULT '无名氏',
qq_mail VARCHAR(20)
);
1.5 PRIMARY KEY:主键约束
指定id列为主键:
sql
-- 重新设置学生表结构
DROP TABLE IF EXISTS student_5;
CREATE TABLE student (
id INT NOT NULL PRIMARY KEY,
sn INT UNIQUE,
name VARCHAR(20) DEFAULT '无名氏',
qq_mail VARCHAR(20)
);
primary key
一般用于主要的字段,如id序号
对于整数类型的主键,常配搭自增长auto_increment来使用。插入数据对于字段不给值时,使用最大值+1
sql
-- 主键是 NOT NULL 和 UNIQUE 的结合,可以不用 NOT NULL
id INT PRIMARY KEY auto_increment
1.6 FOREIGN KEY:外键约束
外键用于关联其他表
的主键 或唯一值
sql
foreign key (字段名) references 主表(列)
案例:
-
创建班级表classes,id为主键
sql-- 创建班级表,有使用MySQL关键字作为字段时,需要使用``来标识 CREATE TABLE classes_5 ( id INT PRIMARY KEY auto_increment, name VARCHAR(20), `desc` VARCHAR(100) );
-
创建学生表student,一个学生对于一个班级,一个班级对应多个学生。使用id为主键,class_id为外键,关联班级表id
sqlDROP TABLE IF EXISTS student_5; CREATE TABLE student ( id INT NOT NULL PRIMARY KEY auto_increment, sn INT UNIQUE, name VARCHAR(20) DEFAULT '无名氏', qq_mail VARCHAR(20), classes_id int, FOREIGN KEY (classes_id) REFERENCES classes_5(id) );
1.7 CHECK约束
MySQL使用时不报错,但忽略该约束(这个约束的使用率不高)
sql
create table test_user (
id int,
name varchar(20),
sex varchar(1),
check (sex = '男' or sex = '女')
);
2. 表的关系
2.1 一对一
sql
-- 一个人只能对应一张身份证,即两个表的id要相同
create table person (
person_id int primary key, name varchar(20)
);
create table id_card (
person_id int primary key,
name varchar(20),
birthday datetime,
address varchar(30),
foreign key (person_id) references person(person_id)
);
2.2 一对多
sql
-- 一个班级可以有多个学生(即能有不同的学生id),所以要为学生设置一个class_id来和class中的id匹配
create table classes (id int primary key, class_name varchar(20));
create table student (
student_id int primary key,
name varchar(20),
class_id int,
foreign key (class_id) references classes(id)
);
2.3 多对多
sql
-- 一个课程中间表(考试成绩表)既要和学生对应,也要和课程对应
create table course (course_id int primary key, course_name varchar(20));
create table score (
id int primary key,
score int,
student_id int,
course_id int,
foreign key (student_id) references student(student_id),
foreign key (course_id) references course(course_id)
);
3. 特殊新增
我们可以将查询到的结果插入到新的表中
语法:
sql
INSERT INTO table_name [(column [, column ...])] SELECT ...
案例:创建一张学生用户表,设计有id学号 name姓名, 年龄age的字段,需要把已有的学生数据复制进来,刚创建且没有被复制赋值的字段为NULL
sql
-- 给学生表插入数据
-- 因为学生表的id已经链接了班级表的id,所以需要先给班级表赋值才能给学生表插入数据
-- (且插入学生表的班级id必须是班级表里能够对应上的)
insert into classes values (707, '计科七班'), (808, '软工八班');
insert into student values (101, '张三', 707), (201, '里斯', 808);
-- 将学生表中能够赋值的数据复制到学生用户表中
insert into test_user (id, name) select student_id, name from student;
4. 查询
4.1 聚合查询
4.1.1 聚合函数
常见的统计总数、计算平均值等操作,可以使用聚合函数来实现,常见的聚合函数有:
函数 | 说明 |
---|---|
COUNT([DISTINCT] expr) | 返回查询到的数据的 数量 |
SUM([DISTINCT] expr) | 返回查询到的数据的 总和,不是数字没有意义 |
AVG([DISTINCT] expr) | 返回查询到的数据的 平均值,不是数字没有意义 |
MAX([DISTINCT] expr) | 返回查询到的数据的 最大值,不是数字没有意义 |
MIN([DISTINCT] expr) | 返回查询到的数据的 最小值,不是数字没有意义 |
-
COUNT
sql-- 统计班级共有多少同学 SELECT COUNT(*) FROM student; -- 统计班级存在的学号一共有几个 SELECT COUNT(class_id) FROM student;
注:当查询结果带有null值时,
非count(*)
的其它count()查询不会将null值的记录计入数据sqlinsert into student(student_id, name) values (125, '小力'); select count(class_id) 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-- 返回英语最低分 select min(english) from exam_result; -- 返回 > 60分 的英语最低分 select min(english) from exam_result where english > 60;
4.1.2 GROUP BY 子句
SELECT 中使用GROUP BY 子句可以对指定列进行分组查询。
条件:使用GROUP BY 进行分组查询时,SELECT指定的字段必须是"分组依据字段",其它字段若想出现在SELECT中则必须包含在聚合函数中。
案例:
-
准备测试表及数据:职员表,有id(主键)、name(姓名)、role(角色)、salary(薪水)
sqlcreate table emp ( id int primary key auto_increment, name varchar(20) not null, role varchar(20) not null, salary numeric(11,2) );
-
查询每个角色的最高工资、最低工资和平均工资
sqlselect role, max(salary), min(salary), avg(salary) from emp group by role;
4.1.3 HAVING
GROUP BY 子句进行分组以后,需要对分组结果再进行条件过滤时 。不能使用WHERE语句,而需要用HAVING
-
显示平均工资低于1500的角色和它的平均工资
sqlselect role, avg(salary) from emp group by role having avg(salary) < 1500;
4.2 联合查询
4.2.1 初始化
实际开发中数据往往来自不同的表,所以需要多表联合查询。多表查询是对多张表的数据取笛卡尔积:
在这里我们先初始化测试数据:
sql
drop table if exists classes;
drop table if exists student;
drop table if exists course;
drop table if exists score;
create table classes (id int primary key auto_increment, name varchar(20), `desc` varchar(100));
create table student (id int primary key auto_increment, sn varchar(20), name varchar(20), qq_mail varchar(20) , classes_id int);
create table course(id int primary key auto_increment, name varchar(20));
create table score(score decimal(3, 1), student_id int, course_id int);
insert into classes(name, `desc`) valu
('22级计科7班', '学习了计算机原理、Python、C、Java、数据结构和算法'),
('19级汉语言3班', '学习了中国传统文学'),
('23级自动化2班', '学习了机械自动化、单片机');
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 操作步骤
在进行多表查询时,一般可分为以下四个步骤:
- 先进行笛卡尔积
- 再指定连接条件
- 其次可指定其它条件
- 最后针对列进行精简
案例:查询"许仙"同学的成绩
-
笛卡尔积
sql-- 指定两个有关联的表进行笛卡尔积 select * from student, score;
-
指定连接条件
sql-- 指定连接条件精简结果(可以通过点【表名.】调用字段) select * from student, score where student.id = score.student_id;
-
再根据需求补充其它条件
sql-- 找许仙的成绩 select * from student, score where student.id = score.student_id and student.name = '许仙';
-
针对上面的最后再进行精简
sqlselect student.name, score.score from student, score where student.id = score.student_id and student.name = '许仙';
注:整个过程也可以总结为一句sql语句来调用
sql
select student.name, score.score from student, score where student.id = score.student_id and student.name = '许仙';
4.2.3 内连接
其操作步骤与上述步骤相同,只是语法不同而已
语法:
sql
select 字段 from 表1 别名 join 表2 别名 on 连接条件 and 其它条件;
--也可以这样做
select 字段 from 表1 别名1, 表2 别名2 where 连接条件 and 其它条件;
注:关联查询可以对关联表使用别名
案例:
-
查询"许仙"同学的成绩
sqlselect stu.name, sco.score from student as stu join score as sco on stu.id = sco.student_id and stu.name = '许仙'; select stu.name, sco.score from student as stu, score as sco where stu.id = sco.student_id and stu.name = '许仙';
-
查询所有同学的总成绩,及同学的个人信息
sql-- 成绩表对学生表是多对一关系,查询总成绩是根据成绩表的同学id来进行分组的 select stu.sn, stu.name, stu.qq_mail, sum(sco.score) from student as stu join score as sco on stu.id = sco.student_id group by sco.student_id;
-
查询所有同学的成绩,及同学的个人信息
这里需要将三张表进行笛卡尔积,先找到表之间的关联关系,将有关联的两张表进行笛卡尔积,再将得到的结果和另一张表计算笛卡尔积
sql-- student 和score 计算笛卡尔积 select stu.id, stu.sn, stu.name, stu.qq_mail, sco.score, sco.course_id, cou.name from student as stu join score as sco on stu.id = sco.student_id join course as cou on sco.course_id = cou.id order by stu.id;
4.2.4 外连接
外连接分为左外连接和右外连接。如果联合查询,左侧的表完全显示我们就说是左外连接;右侧的表完全显示我们就是是右外连接
语法:
sql
-- 左外连接,表1完全显示
select 字段名 from 表名1 left join 表满2 on 连接条件;
-- 右外连接,表2完全显示
select 字段名 from 表名1 right join 表满2 on 连接条件;
案例:查询所有同学的成绩,及同学的个人信息,如果该同学没有成绩,也需要显示
sql
-- "老外学中文"同学 没有考试成绩,也显示出来了
select * from student as stu left join score as sco on stu.id = sco.student_id;
-- 对应的右外连接
select * from student as stu right join score as sco on stu.id = sco.student_id;
4.2.5 自连接
自连接是指在同一张表上连接自身进行查询
案例: 显示所有"计算机原理"成绩比"Java"成绩高的成绩信息
sql
-- 先查询"计算机原理"和"Java"课程的id
select id, name from course where name = 'Java' or name = '计算机原理';
-- 再查询成绩表中,"计算机原理"成绩比"Java"成绩好的信息
select s1.* from score as s1, score as s2 where s1.student_id = s2.student_id and s1.score < s2.score and s1.course_id = 1 and s2.course_id = 3;
4.2.6 合并查询
在实际应用中,为了合并多个select的执行结果,可以使用集合操作符union
,union all
。使用union 和 union all 时,前后查询的结果集中,字段需要一致(union左右两侧sql可以是查询两个不同的表,而or不行)
-
union
该操作符用于取得两个结果集的并集。当使用该操作符时,会自动去掉结果集中的重复行。
案例:查询id小于3,或者名字为"英文"的课程:
sqlselect * from course where id < 3 union select * from course where name = '英文';
-
union all
该操作符用于取得两个结果集的并集。当使用该操作符时,不会去掉结果集中的重复行。
案例:查询id小于3,或者名字为"Java"的课程
sql-- union all的结果集中出现重复的数据Java select * from course where id < 3 union all select * from course where name = 'Java';