多表关联关系
-
一对一关系
通过主键关联主键实现
通过外键关联主键可以实现
案例:游戏用户(user)和账号(game_num)
-
一对一实现
第一种方式,通过主键关联主键实现,建完表后添加外键 用户表user create table user( id int primary key auto_increment, name varchar(50), age int); 账号表game_num create table game_num( id int primary key auto_increment, nickname varchar(50), level int); 没有添加外键约束可以任意删除 外键 alter table game_num add constraint foreign key(id) references user(id); constraint可以省略 添加数据 insert into user values(1,"张伟强",22); insert into game_num values(1,"国服李白",15); insert into game_num values(2,"国服韩信",25); --error 第二种方式实现:建表的时候添加外键 用户表user create table user( id int primary key auto_increment, name varchar(50), age int); 账号表game_num create table game_num( id int primary key auto_increment, nickname varchar(50), level int, foreign key(id) references user(id) );
-
一对多关系
通过外键关联可以实现,外键添加可以添加在多的一方
中间表进行关联,多的一方设置唯一约束
案例:店铺(shop),商品(goods)
通过外键关联可以实现,外键添加可以添加在多的一方 创建店铺表 create table shop( id int primary key, sname varchar(50) ); 创建商品表 create table goods( id int primary key, gname varchar(50), price float, sid int ); 添加外键约束 alter table goods( add foreign references shop(id) ); insert into shop values(1,"小米旗舰店"); insert into goods values(1,"小米10",3999,1); insert into goods values(2,"平衡车",2999,1); insert into goods values(3,"小米笔记本",5999,1); 中间表进行关联,多的一方设置唯一约束 创建店铺表 create table shop( id int primary key, sname varchar(50) ); 创建商品表 create table goods( id int primary key, gname varchar(50), price float, ); 关系表 create table shop_goods( id int primary key, sid int, foreign key(sid) references shop(id), gid int unique, foreign key(gid) references goods(id) ); insert into shop values(1,"小米旗舰店"); insert into goods values(1,"小米10",3999); insert into goods values(2,"平衡车",2999); insert into goods values(3,"小米笔记本",5999); insert into shop_goods values(1,1,1); insert into shop_goods values(2,1,2); insert into shop_goods values(3,1,3);
-
多对对关系
- 中间表多对多关联,不设置唯一约束即可
- 案例:学生(stu)和课程(course)
创建学生表 create table stu( id int primary key, sname varchar(50) ); 创建课程表 create table course( id int primary key, cname varchar(50) ); 关系表 create table stu_course( id int primary key, sid int, foreign key(sid) references stu(id), cid int, foreign key(cid) references course(id) ); 张伟强选了 语文,数学和英语 张威选了 数学和物理 插入学生数据 insert into stu values(1,"张伟强"); insert into stu values(2,"张威"); 插入课程数据 insert into course values(1,"语文"),(2,"数学"),(3,"英语"),(4,"物理"); 建立关系 insert into stu_course values(1,1,1); insert into stu_course values(2,1,2); insert into stu_course values(3,1,3); insert into stu_course values(4,2,1); insert into stu_course values(5,2,4);
多表连接查询
-
比单表查询会耗费更多的系统资源
创建两个测试表a和b create table a(id int,name varchar(10)); create table b(id int,name varchar(10)); insert into a values(1,"a") insert into a values(2,"b") insert into a values(4,"d") insert into b values(1,"x") insert into b values(2,"y") 分类 交叉连接 不指定条件连接,结果集就是笛卡尔积,记录数就是a表记录m乘以b表记录n select * from a cross join b; select * from a join b; 内连接 指定条件连接,结果集包含符合连接条件的记录数 select * from a inner join b on a.id=b.id; select * from a join b on a.id=b.id; 左连接(左外连接) 指定条件连接,结果集包含左表全部记录,右表中符合连接条件的记录和右表中不符合连接条件的记录(用NULL填充) select * from a left outer join b on a.id=b.id; select * from a left join b on a.id=b.id; 右连接(右外连接) 指定条件连接,结果集包含右表全部记录,左表中符合连接条件的记录和左表中不符合连接条件的记录(用NULL填充) select * from a right outer join b on a.id=b.id; select * from a right join b on a.id=b.id;
-
案例操作
查看 近卫 部门所有员工信息 select * from employee,department where employee.deptno=department.deptno and department.deptname="近卫"; 查询每个部门有哪些员工(内连接) select * from employee e inner join department d on e.deptno=d.deptno; 查询每个部门有哪些员工(左连接) select * from employee e left join department d on e.deptno=d.deptno; 查询每个部门有哪些员工(右连接) select * from employee e right join department d on e.deptno=d.deptno; stu和class多表连接查询 例1:显示学生的学号,姓名,性别,班号,班级名称以及对应班主任,按照班号和学号排序 select s.sno 学号,s.sname 姓名,s.sex 性别,s.cno 班号,c.cname 班级名称,c.teacher 班主任 from stu s inner join class c on s.cno=c.cno order by s.cno,s.sno; 例2:显示学生的学号,姓名,性别,班号,班级名称以及对应班主任,无班主任的显示“暂无”,按照班号和学号排序 select s.sno 学号,s.sname 姓名,s.sex 性别,s.cno 班号,c.cname 班级名称,if(c.teacher is null,"暂无",c.teacher) 班主任 from stu s left join class c on s.cno=c.cno order by s.cno,s.sno; select s.sno 学号,s.sname 姓名,s.sex 性别,s.cno 班号,c.cname 班级名称,ifnull(c.teacher,"暂无") 班主任 from stu s left join class c on s.cno=c.cno order by s.cno,s.sno;
-
复合查询
-
用集合运算符对多个查询结果进行运算,产生新的查询结果集
对两个结果进行并集操作 并集 union,union all 交集 差集 示例: 创建class1表 create table class1 as select * from class where cno=1; insert into class1 values(5,'5班',"张伟强"); 例1:查看class和class1表的并集,重复记录,重复显示 select * from class union all select * from class1; 例2:查看class和class1表的交集 select c.cno,c.cname,c.teacher from class c join class1 c1 on c.cno=c1.cno; 例3:查看class和class1表的差集,class中有,class1没有的 1.左连接查询 select * from class c left join class1 c1 on c.cno=c1.cno; 2.去除相同行 select * from class c left join class1 c1 on c.cno=c1.cno where c1.cno is null; 3.取出右边的显示 select c.cno,c.cname,c.teacher from class c left join class1 c1 on c.cno=c1.cno where c1.cno is null; 例4:查看class和class1表的差集,class1中有,class没有的 1.右连接查询 select * from class c right join class1 c1 on c.cno=c1.cno; 2.去除相同行 select * from class c right join class1 c1 on c.cno=c1.cno where c.cno is null; 3.取出右边的显示 select c1.cno,c1.cname,c1.teacher from class c right join class1 c1 on c.cno=c1.cno where c.cno is null;
-
-
子查询
结构
select 列1,列2...from 表名 where 列=值 group by 分组列 having 分组列=值 order by 列
使用一个查询语句实现更复杂的查询,嵌套的select 往往被称为子查询,需要用()括起来
-
非关联子查询:子查询可以单独用于主查询执行,仅执行一次,效率较高
-
关联子查询:子查询不可以单独用于主查询执行,如果主查询有N行,子查询将执行N次,效率相对较低,但是灵活度高
-
非关联子查询 例1:查询学生中哪些人比张飞的体重重? select weight from stu where sname="张飞"; select * from stu where weight>(select weight from stu where sname="张飞"); 例2:2班3班中哪些同学的身高比1班的平均身高高? select avg(weight) from stu where cno=1; select * from stu where cno in (2,3) and height>(select avg(weight) from stu where cno=1); 例3:每个班的高考状元 select cno,max(score) from stu group by cno; select * from stu where (cno,score) in (select cno,max(score) from stu group by cno) 例4:哪些同学的体重比所有班的平均体重重? select avg(weight) from stu group by cno; select * from stu where weight>all(select avg(weight) from stu group by cno); all :比所有的值都大 any:比所有的值都小 例5:哪些同学的身高高于本班平均身高? select cno,avg(height) from stu where cno is not null group by cno; select * from stu s,(select cno,avg(height) havg where cno is not null group by cno) a where s.cno=a.cno and s.height>a.havg and s.cno is not null; 例6:不用多表连接方式,列出3班的学生姓名和3班的班主任 select s.sname 学生姓名,(select teacher from class where cno=3) 班主任 from stu s where s.cno=3;
-
关联子查询 例1:不用多表连接的方式,列出每个学生的班号,姓名,和所在班的班主任 select s.cno 班号,s.sname 姓名,(select teacher from class where cno=s.cno) from stu s where s.cno is not null order by 1; #子查询不能够单独执行,主查询一般需要起别名 例2:使用关联子查询,在已分班的学生中列出身高高于本班平均身高的学生 select * from stu s1 where s1.cno is not null and s1.height>(select avg(height) from stu s2 where s2.cno=s1.cno); 例3:体重最重的学生的班主任是谁? 1.最重的体重 select max(weight) from stu; 2.查询每个学生的班主任 select sname,weight,teacher from stu,class where stu.cno=class.cno; 3.加上条件 select sname,weight,teacher from stu,class where stu.cno=class.cno and weight=(select max(weight) from stu); 练习1.从学生表中和班级表中找出姓“曹”的人,标注其角色,学生或老师 select sname from stu where sname like "曹%"; select sname,"学生" 角色 from stu where sname like "曹%"; select teacher,"老师" 角色 from class where teacher like "曹%"; 取并集 union all select sname,"学生" 角色 from stu where sname like "曹%" union all select teacher,"老师" 角色 from class where teacher like "曹%";
-
事务
-
事务是一种机制,一个操作序列,包含了一组数据库操作命令。事务是一个不可分割的工作逻辑单元,在数据库执行并发操作时,事务是最小的控制单元。
-
A用户和B用户是银行的储户,现在A要给B转500块钱 检查账户余额>=500元 A账户扣除500元 B账户增加500元 正常流程走下来,A账户扣除500元,B账户增加500元 如果A扣了钱之后,系统出了故障,A损失了500,B没有收到500元 所谓事务,就是一个操作序列要么都执行,要么都不执行,是一个不可分割的工作单位
-
数据库引擎
-
数据库存储引擎,是数据库底层构建的核心,负责底层数据持久化和软件交互的序列化操作、
校验过程以及交互过程,通过数据库存储引擎完成创建、查询、更新和删除数据。不同的存
储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎,还可以 获
得特定的功能。现在许多不同的数据库管理系统都支持多种不同的数据引擎。
-
InnoDB
InnoDB 是事务型数据库的首选引擎,支持事务安全表(ACID),支持行锁定和外键,上
图也看到了,InnoDB 是默认的 MySQL 引擎。
- MyISAM
MyISAM 基于 ISAM 存储引擎,并对其进行扩展。它是在 Web、数据仓储和其他应用环境下
最常使用的存储引擎之一。MyISAM 拥有较高的插入、查询速度,但不支持事物。
-
-
事务的特性
- 原子性(atomicity)
- 一致性(consistency)
- 隔离性(isolation)
- 持久性(durability)
原子性:一个事务必须视为一个不可分割的最小的工作单元,整个事务中的所有操作要么全部执行成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作 一致性:从一个一致性的状态转换到另一个一致性的状态,如现在A要给B转500块钱,A账户扣除500元,B账户增加500元 隔离性:同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。 持久性:一旦事务提交,其所做的修改会永久的保存到数据库
-
事务的隔离级别
如果数据库严格按照四大特性进行管理,数据库的操作会比较缓慢
Read Uncommitted(读未提交) 脏读,不可重复读 操作效率最高,数据稳定性最差 一个事务可以读取到另一个事务未提交的更新结果 Read committed(读已提交) 不可重复读,幻读 操作效率较高,数据稳定性较差 一个事务可以读取到另一个事务已提交的更新结果 Repeat Read(可重复读) 幻读 操作效率较低,数据稳定性较差 在整个事务过程中,对同一个数据的读取结果是相同的,不管其他事务是否同时在对同一笔数据进行更新,提交 Serializable(可串行化)序列化 操作效率最低,数据稳定性最高 所有的操作,全部排队,任何事情必须等待前一件事情全部完成才能开始
-
事务的并发问题
脏读(dirty read):A 事务读取 B 事务尚未提交的更改数据,并在这个数据基础上操作。如 果 B 事务回滚,那么 A 事务读到的数据根本不是合法的,称为脏读。 不可重复读(unrepeatable read):A 事务读取了 B 事务已经提交的更改(或删除)数据。比 如 A 事务第一次读取数据,然后 B 事务更改该数据并提交,A 事务再次读取数据,两次读取 的数据不一样。 幻读(phantom read):A 事务读取了或意识到了 B 事务已经提交的新增数据。注意和不可重复读的区别,这里是新增,不可重复读是更改(或删除)。 幻读的重点在于新增或者删除(数据条数的变化) 目前5000元工资的员工有10人 事务1,读取工资为5000的员工 事务2,向数据库表插入一条员工记录,工资也是5000 事务1,再次读取工资为5000的员工
-
事务的操作
开启事务 begin; 或者 start transaction 提交事务 commit; 回滚事务 rollback; 设置事务的隔离级别 set session transaction isolation leval 隔离级别;
-
案例
create tabel user( id int primary key auto_increment, name varchar(20)); 一、读未提交与脏读 终端1 设置事务隔离级别并开启,终端2,设置事务隔离级别并开启 set session transaction isolation level read uncommitted; 开启事务 begin; 插入数据 insert into user values(1,"张威"); 1还没有提交,2就可以读取到数据 二、读已提交和不可重复读 终端1 设置事务隔离级别并开启,终端2,设置事务隔离级别并开启 set session transaction isolation level read committed; 开启事务 begin; 插入数据 insert into user values(1,"张威"); 1还没有提交,2不可以读取到数据,但A提交之后,B就读取到了 三、可重复读 终端1 设置事务隔离级别并开启,终端2,设置事务隔离级别并开启 set session transaction isolation level repeatable read; 开启事务 begin; 插入数据 insert into user values(1,"张威"); 1还没有提交,2不可以读取到数据,但A提交之后,B就读取到了 无法避免幻读 在1插入数据前,2查看user为空 在1插入数据后并并提交,2查看user为空 在2中执行insert into user values(1,"张威"); 出现主键冲突,插入不进去,仿佛数据存在但是看不到,出现了幻觉
索引
-
索引:数据库中的一个数据对象,用来提升查询效率
-
索引的种类
普通索引:可以为空,可以重复 主键索引:不可为空,不可以重复 唯一索引:可以为空,不可以重复
-
索引的优点和缺点
索引的优点:创建索引可以大大提高系统的性能。 • 第一,通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。 • 第二,可以大大加快数据的检索速度,这也是创建索引的最主要的原因。 • 第三,可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。 • 第四,在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。 索引的缺点:增加索引也有许多不利的方面 • 第一,创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。 • 第二,索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。 • 第三,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就 降低了数据的维护速度。
-
索引的使用
普通索引 ALTER TABLE 表名 ADD INDEX(字段名); 唯一索引 ALTER TABLE 表名 ADD UNIQUE(字段名); 主键索引 主键本身就是主键索引,我们创建表,添加了主键,就相当于添加了主键索引 ALTER TABLE 表名 ADD PRIMARY KEY(字段名); 多列索引 ALTER TABLE 表名 ADD INDEX(字段名,字段名,字段名); 删除索引 DROP INDEX 索引名 ON 表名;删除主键索引需要先删除主键的自增属性 注意:由于索引和数据是存储在同一个文件中,因此在使用独立表空间时,InnoDB引擎使用drop命令删除索引并不会释放磁盘空间 ALTER TABLE 表名 DROP PRIMARY KEY 查看索引 SHOW INDEX FROM student;
-
百万数据查询实验
存储过程 delimiter $$ #存储过程名proc1,参数cnt为准备插入数据行的数量,调用时输入该值 create procedure proc1(cnt int) begin #定义变量i为整型,默认值为1 declare i int default 1; #开启事务 start transaction; #MySQL repeat循环结构 repeat #插入test库t表,id列值对应为i, #name列对应值为字符‘a’与i值合并后的字符串 insert into test.t (id,name) values (i,concat('a',i)); #变量i自增1 set i = i + 1; #当i值大于输入cnt值时,退出循环体 until i > cnt end repeat; commit; end$$ delimiter ; 创建数据库和数据表 create database test; use test; create table t(id int,name varchar(30)); 调用存储过程,设置插入一百万行数据 call proc1(1000000);
实验一:百万记录取1条 select * from t where id=1; select * from t where id=900000; 创建索引后再查询 create unique index idx_id on t(id); drop index idx_id on t;
-
总结:
主要优点使select查询速度大幅度提高,包括更新少量数据
副作用:1.占用额外的磁盘空间
2.使得数据表的增删改操作变慢
-