Day4 MySQL 学习笔记
通过**“2024版MySQL数据库基础+进阶全套视频教程完整版”, 作者:一名00后程序媛,Bilibili.com **学习
6.5外键约束
将一个列添加外键约束与另一张表的主键(唯一列)进行关联后,这个外键约束的列添加的数据必须要在关联的主键字段中存在
案例:学生表与班级表(在学生表添加外键,与班级表主键关联)
-
创建班级表(与其他表没有关系的表)
create table classes( classID int primary key auto_increment, classname varchar(40) not null unique, classremark varchar(200) );
-
创建学生表(在学生表添加外键,与班级表主键关联)
#方式一:创建表时,添加外键约束,定义cid字段,添加外键约束 #cid列要与classes表中的id关联,所以cid的类型要与和长度要和id一致。 create table students( stunum char(8) primary key, stuname varchar(20) not null, stugender char(2) not null, stuage int not null, cid int, constraint FK_STUDENTS_CLASSE foreign key(cid) references classes(classID) ); #方式二:先创建表,再添加外键约束 create table students( stunum char(8) primary key, stuname varchar(20) not null, stugender char(2) not null, stuage int not null, cid int, ); alter table students add constraint FK_STUDENTS_CLASSE foreign key(cid) references classes(classID); #删除外键约束 alter table students drop foreign key FK_STUDENTS_CLASSE;
-
向班级表添加班级信息
insert into clases(classname,classremark) values('badminton','...'); insert into clases(classname,classremark) values('basketball','...'); insert into clases(classname,classremark) values('Java','...'); insert into clases(classname,classremark) values('python','...'); select * from classes;
-
向学生表添加学生信息
insert into students(stunum,stuname,stugender,stuage,cid) values('2020305','小美','female','20','4'); #添加学生时,设置给cid外键列的值必须在其关联的主表中存在,下面这个会失败。 insert into students(stunum,stuname,stugender,stuage,cid) values('2020305','小美','female','20','6');
学生表中存在学生信息关联班级表的某条记录时,就不能对班级表中的这条记录进行修改ID和删除操作
如果一定要修改classID:
- 将引用classID的学生记录的cid修改为null
- 修改班级表中classID的记录
- 将学生表中设置为null的cid修改为这个班级的新的id
update students set cid = null where cid=1;
update classes set classID=5 where classname='Java';
update students set cid=5 where cid is null;
我们还可以使用级联操作来实现:
1.添加外键时候设置级联修改和级联删除
#删除原有外键
alter table students drop foreign key FK_STUDENT_CLASSES;
#重新添加外键,设置级联修改和删除
alter table students add constraint FK_STUDENTS_CLASSE foreign key(cid) references classes(classID) ON UPDATE CASCADE ON DELETE CASCADE;
#就可以对班级表的ID进行修改和删除了. 直接修改classID,关联这条记录的学生记录的cid也会同步修改
update classes set classID=1 where classname='Java';
#删除classID=1的班级记录,关联这条记录的学生记录也会同步删除
delete from classes where classID=1;
7.连接查询
通过连接查询从多张数据表提取数据
使用join:
- inner join 内连接
- left join 左连接
- right join 右连接
7.1数据准备
创建数据表
create table classes(
classID int primary key auto_increment,
classname varchar(40) not null unique,
classremark varchar(200)
);
create table students(
stunum char(8) primary key,
stuname varchar(20) not null,
stugender char(2) not null,
stuage int not null,
cid int,
constraint FK_STUDENTS_CLASSE foreign key(cid) references classes(classID) ON UPDATE CASCADE ON DELETE CASCADE
);
添加数据
#badminton包含3个学生信息
insert into clases(classname,classremark) values('badminton','...');
#basketball包含2个学生信息
insert into clases(classname,classremark) values('basketball','...');
#没有学生信息的班级
insert into clases(classname,classremark) values('Java','...');
insert into clases(classname,classremark) values('python','...');
#ABC属于badminton
insert into students(stunum,stuname,stugender,stuage,cid) values('2020301','A','female','20','1');
insert into students(stunum,stuname,stugender,stuage,cid) values('2020302','B','female','20','1');
insert into students(stunum,stuname,stugender,stuage,cid) values('2020303','C','male','20','1');
#DE属于basketball
insert into students(stunum,stuname,stugender,stuage,cid) values('2020304','D','female','20','2');
insert into students(stunum,stuname,stugender,stuage,cid) values('2020305','E','male','20','2');
#FG没有cid,没有设置班级信息
insert into students(stunum,stuname,stugender,stuage) values('2020306','F','male','20');
insert into students(stunum,stuname,stugender,stuage) values('2020307','G','female','20');
7.2 inner join
select ... from tableName1 inner join tableName2;
-
笛卡尔积
- 笛卡尔积(A集合&B集合):使用A中的每个记录关联B中的每个记录,笛卡尔积的总数=A总数*B总数
- 如果直接执行 select … from tableName1 inner join tableName2; 会获取两种数据表中的数据集合的笛卡尔积(依次使用table1中的一条记录去匹配table2中的每条记录)
-
内连接条件
如何消除笛卡尔积数据中的无意义数据——添加两张表进行连接查询时的条件
-
使用on设置两张表连接查询的匹配条件
#用where:先生成笛卡尔积再从笛卡尔积中过滤数据,低效率 select * from students inner join classes where student.cid=classes.classID; #用on:先判断条件,如果成立,两张表数据进行组合生成一条结果记录 select * from students inner join classes on student.cid=classes.classID;
-
结果:只获取两种表中匹配条件成立的数据,任何一张表在另一张表如果没有找到对应匹配则不会出现在查询结果中。(如:FG没有对应班级信息,java和python没有学生信息,所以不出现在结果中)
7.3左连接
查询所有学生信息,如果学生有对应的班级信息,则将对应的班级信息也查询出来
显示左表的所有数据,如果在右表中存在与左表记录满足匹配条件的数据则匹配,如果不存在匹配数据,显示为null。
select* from lefttabke left join righttable on [condition];
select * from students left join classes on students.cid=classes.classID;
7.4 右连接
显示右表的所有数据,如果在左表中存在与左表记录满足匹配条件的数据则匹配,如果不存在匹配数据,显示为null。
select * from students right join classes on students.cid=classes.classID;
7.5 数据表别名
如果在连接查询的多张表中存在相同名字的字段,可以使用(表名.字段名)来区分,如果表名太长,可以使用数据表别名
select s.*,c.classname
from students s
inner join classes c
on s.cid = c.classID;
7.6子查询/嵌套查询
先进行一次查询,第一次的查询结果作为第二次查询的源/条件。第二次查询基于第一次查询结果
示例1:查询班级badminton中的学生信息(只知道班级名称,不知道班级id)
子查询返回结果是单行单列
- 传统方式
select classID from classes where classname='badminton';
#得到班级编号
select * from students where cid=1
- 子查询
select *from students where cid = (select classID from classes where clasname='badminton');
示例2:如果有很多个java班级(java1,java2…) 查询所有java班的学生信息
子查询返回结果是单列多行
- 传统方式
select classID from classes where classname like 'Java%';
#比如得到了classID=1,2,3
select * from students where cid=1
union
select * from students where cid=2
union
select * from students where cid=3;
- 子查询
#如果子查询返回结果(classID)是多个值(单列多行),条件使用in/not in
select * from students where cid in (select classID from classes where classname like 'Java%');
示例3:查询cid=1班级中性别为男的学生信息
子查询返回结果是多行多列
#多条件查询
select *from students where cid=1 and stugender='male';
#子查询:先查询cid=1的所有学生,将这些信息作为一个虚拟表(多行多列)再基于这个虚拟表查询性别为男的学生信息。
select * from (select * from students where cid=1) t where t.stugender='male';
8.存储过程
8.1存储过程介绍
从SQL执行的流程中分析存在的问题:
- 如果需要重复多次执行相同的SQL,SQL执行都需要通过连接传递到MySQL,并需要经过编译和执行的步骤
- 如果需要连续执行多个SQL指令,并且第二个SQL指令需要使用第一个SQL指令执行的结果作为参数。
存储过程
将能够完成特定功能的SQL指令进行封装(SQL指令集),编译之后储存在数据库服务器上,并且为之去一个名字,客户端可以通过名字直接调用指令集。
优点:
- SQL指令无需客户端编写,节省网络开销,避免SQL指令在网络传输中被篡改
- 存储过程是经过编译创建并保存至数据库中的,执行过程中无需重读进行编译操作,性能提升
- 存储过程中多个SQL指令之间存在逻辑关系,支持流程控制语句(分支,循环)
缺点:
- 存储过程时根据不同数据库进行编译创建并存储在数据库中的,当切换到其他数据库产品时,需要重新编写存储过程
- 存储过程受限于数据库产品
- 如果需要数据库的高并发访问,使用存储过程会增加数据库的连续执行时间。(我们将复杂的业务交给了数据库进行处理)
8.2 创建存储过程
create procedure <procedurename> ([in/out args])
begin
#SQL
end;
示例:
# in是输入参数,out是输出参数
create procedure proc1 (in a int, in b int,out c int)
begin
set c = a+b;
end;
8.3 调用存储过程
#定义变量 @m
set @m=0;
#调用存储过程,3--a,2--b,@m--c
call proc1(3,2,@m);
#显示变量值
select @m from dual;
8.4 存储过程中变量的使用
分为局部变量和用户变量
1.定义局部变量
局部变量:定义在存储过程中的变量,只在存储过程内部使用
#定义在存储过程中的开始
declare <attr_name> <type> [default type];
示例:
create procedure proc2 (in a int, out r int)
begin
declare x int default 0;
declare y int default 1;
set x = a*a
set y = a/2
set r = x+y;
end;
2.定义用户变量
用户变量:全局变量,定义的用户变量可以通过 select @attrName from dual 进行查询
#用户变量存储在mysql数据库的数据字典(dual)中
#通过set关键词定义,变量名要以@开头
set @n=1;
- 给变量设置值
- 两种变量都使用set关键字修改值
set @n=1;
call proc2(6,@n)
select @n from dual;
c2 (in a int, out r int)
begin
declare x int default 0;
declare y int default 1;
set x = a*a
set y = a/2
set r = x+y;
end;
2.定义用户变量
用户变量:全局变量,定义的用户变量可以通过 select @attrName from dual 进行查询
```sql
#用户变量存储在mysql数据库的数据字典(dual)中
#通过set关键词定义,变量名要以@开头
set @n=1;
- 给变量设置值
- 两种变量都使用set关键字修改值
set @n=1;
call proc2(6,@n)
select @n from dual;