MySQL笔记-多表查询

本文标签 : 多表查询  事务四大特性  并发事务问题  事务隔离级别

文章目录

目录

文章目录

一、多表查询

1.多表关系

2.多表查询概念

 3.多表查询的分类

 4.内连接

 5.外连接

6.自连接

 7.联合查询

 8.子查询  

1.标量子查询

2.列子查询

3.行子查询

4.表子查询

 9.多表查询案例练习

二、事务

1.事务简介

 2.事务操作 

 3.事务四大特性

4.并发事务引发的问题

 5.事务隔离级别,解决事务并发问题

总结


一、多表查询

1.多表关系

  • 概述:
  • 一对多(多对一): 
  • 多对多:   

演示:

-- -------------------------------- 多表关系 演示 ---------------------------------------------

-- 多对多 ----------------
create table student(
    id int auto_increment primary key ***ment '主键ID',
    name varchar(10) ***ment '姓名',
    no varchar(10) ***ment '学号'
) ***ment '学生表';
insert into student values (null, '黛绮丝', '2000100101'),(null, '谢逊', '2000100102'),(null, '殷天正', '2000100103'),(null, '韦一笑', '2000100104');


create table course(
    id int auto_increment primary key ***ment '主键ID',
    name varchar(10) ***ment '课程名称'
) ***ment '课程表';
insert into course values (null, 'Java'), (null, 'PHP'), (null , 'MySQL') , (null, 'Hadoop');


create table student_course(
    id int auto_increment ***ment '主键' primary key,
    studentid int not null ***ment '学生ID',
    courseid  int not null ***ment '课程ID',
    constraint fk_courseid foreign key (courseid) references course (id),
    constraint fk_studentid foreign key (studentid) references student (id)
)***ment '学生课程中间表';

insert into student_course values (null,1,1),(null,1,2),(null,1,3),(null,2,2),(null,2,3),(null,3,4);

student_course : 

student :

course :

 

关系视图 : 

 

  •  一对一 :

演示: 

-- --------------------------------- 一对一 ---------------------------
create table tb_user(
    id int auto_increment primary key ***ment '主键ID',
    name varchar(10) ***ment '姓名',
    age int ***ment '年龄',
    gender char(1) ***ment '1: 男 , 2: 女',
    phone char(11) ***ment '手机号'
) ***ment '用户基本信息表';

create table tb_user_edu(
    id int auto_increment primary key ***ment '主键ID',
    degree varchar(20) ***ment '学历',
    major varchar(50) ***ment '专业',
    primaryschool varchar(50) ***ment '小学',
    middleschool varchar(50) ***ment '中学',
    university varchar(50) ***ment '大学',
    userid int unique ***ment '用户ID',
    constraint fk_userid foreign key (userid) references tb_user(id)
) ***ment '用户教育信息表';


insert into tb_user(id, name, age, gender, phone) values
        (null,'黄渤',45,'1','18800001111'),
        (null,'冰冰',35,'2','18800002222'),
        (null,'码云',55,'1','18800008888'),
        (null,'李彦宏',50,'1','18800009999');

insert into tb_user_edu(id, degree, major, primaryschool, middleschool, university, userid) values
        (null,'本科','舞蹈','静安区第一小学','静安区第一中学','北京舞蹈学院',1),
        (null,'硕士','表演','朝阳区第一小学','朝阳区第一中学','北京电影学院',2),
        (null,'本科','英语','杭州市第一小学','杭州市第一中学','杭州师范大学',3),
        (null,'本科','应用数学','阳泉第一小学','阳泉区第一中学','清华大学',4);

tb_user :

tb_user_edu :


2.多表查询概念

-- ------------------------------------> 多表查询 <--------------------------------------------
-- 准备数据
create table dept(
    id   int auto_increment ***ment 'ID' primary key,
    name varchar(50) not null ***ment '部门名称'
)***ment '部门表';

create table emp(
    id  int auto_increment ***ment 'ID' primary key,
    name varchar(50) not null ***ment '姓名',
    age  int ***ment '年龄',
    job varchar(20) ***ment '职位',
    salary int ***ment '薪资',
    entrydate date ***ment '入职时间',
    managerid int ***ment '直属领导ID',
    dept_id int ***ment '部门ID'
)***ment '员工表';

-- 添加外键
alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id);

INSERT INTO dept (id, name) VALUES (1, '研发部'), (2, '市场部'),(3, '财务部'), (4, '销售部'), (5, '总经办'), (6, '人事部');
INSERT INTO emp (id, name, age, job,salary, entrydate, managerid, dept_id) VALUES
            (1, '金庸', 66, '总裁',20000, '2000-01-01', null,5),

            (2, '张无忌', 20, '项目经理',12500, '2005-12-05', 1,1),
            (3, '杨逍', 33, '开发', 8400,'2000-11-03', 2,1),
            (4, '韦一笑', 48, '开发',11000, '2002-02-05', 2,1),
            (5, '常遇春', 43, '开发',10500, '2004-09-07', 3,1),
            (6, '小昭', 19, '程序员鼓励师',6600, '2004-10-12', 2,1),

            (7, '灭绝', 60, '财务总监',8500, '2002-09-12', 1,3),
            (8, '周芷若', 19, '会计',48000, '2006-06-02', 7,3),
            (9, '丁敏君', 23, '出纳',5250, '2009-05-13', 7,3),

            (10, '赵敏', 20, '市场部总监',12500, '2004-10-12', 1,2),
            (11, '鹿杖客', 56, '职员',3750, '2006-10-03', 10,2),
            (12, '鹤笔翁', 19, '职员',3750, '2007-05-09', 10,2),
            (13, '方东白', 19, '职员',5500, '2009-02-12', 10,2),

            (14, '张三丰', 88, '销售总监',14000, '2004-10-12', 1,4),
            (15, '俞莲舟', 38, '销售',4600, '2004-10-12', 14,4),
            (16, '宋远桥', 40, '销售',4600, '2004-10-12', 14,4),
            (17, '陈友谅', 42, null,2000, '2011-10-12', 1,null);

查询两张表的数据     直接查询的结果:  可以看到有大量的无效的数据

要想从 emp 表和 dept 表中查询数据 ,要先对无效的笛卡尔积进行消除 :

-- 多表查询 -- 笛卡尔积
select * from emp , dept where emp.dept_id = dept.id;

清除后 : 

  


 3.多表查询的分类


 4.内连接

演示  

-- 内连接演示
-- 1. 查询每一个员工的姓名 , 及关联的部门的名称 (隐式内连接实现)
-- 表结构: emp , dept
-- 连接条件: emp.dept_id = dept.id
select emp.name , dept.name from emp , dept where emp.dept_id = dept.id ;
# 给表起别名,不能再通过表名来限定字段
select e.name,d.name from emp e , dept d where e.dept_id = d.id;


-- 2. 查询每一个员工的姓名 , 及关联的部门的名称 (显式内连接实现)  --- INNER JOIN ... ON ... (inner 可以省略)
-- 表结构: emp , dept
-- 连接条件: emp.dept_id = dept.id

select e.name, d.name from emp e inner join dept d  on e.dept_id = d.id;

select e.name, d.name from emp e join dept d  on e.dept_id = d.id;

结果 : 


 5.外连接

演示 : 

-- 外连接演示
-- 1. 查询emp表的所有数据, 和对应的部门信息(左外连接) (会完全的显示左表内容以及和右表交集的查询内容)
-- 表结构: emp, dept
-- 连接条件: emp.dept_id = dept.id
select e.*,d.name from emp e left outer join dept d on e.dept_id = d.id;

select e.*, d.name from emp e left join dept d on e.dept_id = d.id;

-- 2. 查询dept表的所有数据, 和对应的员工信息(右外连接) (会完全的显示右表内容以及和左表交集的查询内容)
select d.*, e.* from emp e right outer join dept d on e.dept_id = d.id;
# 如果改成左外连接,只需调换一下左右表的顺序即可
select d.*, e.* from dept d left outer join emp e on e.dept_id = d.id;

结果 : 

 


6.自连接

演示 :

自连接可以将自身看成两张表 a 和 b ,a 表 和 b 表 的连接条件是 

    a表的 managerid = b表的 id

演示 : 

-- 自连接
-- 1. 查询员工 及其 所属领导的名字
-- 表结构: emp  (必须起别名 可以理解为使用内连接查询表交集部分的数据)

select a.name,b.name from emp a , emp b where a.managerid = b.id;

-- 2. 查询所有员工 emp 及其领导的名字 emp , 如果员工没有领导, 也需要查询出来
-- 表结构: emp a , emp b  (没有领导也要查询出来,可以理解为使用外连接查询完整的表)

select a.name '员工',b.name '领导' from emp a left outer join emp b on a.managerid = b.id;

结果 : 


 7.联合查询

演示 : 

-- union all , union
-- 1. 将薪资低于 5000 的员工 , 和 年龄大于 50 岁的员工全部查询出来.

-- 直接将查询的两个结果合并
select * from emp where salary < 5000
union all
select * from emp where age > 50;

-- 去除重复数据 可以删去关键字 all
select * from emp where salary < 5000
union
select * from emp where age > 50;

注意 : 联合查询的多张表的列数必须保持一致 ,字段类型也要保持一致.

结果 :


 8.子查询  

1.标量子查询

演示 : 

-- -------------------------------------- 子查询 ------------------------

-- 标量子查询
-- 1. 查询 "销售部" 的所有员工信息
-- 第一步. 查询 "销售部" 部门ID
select id from dept where name = '销售部';

-- 第二步. 查询部门 id 为 4 查询员工信息
select * from emp where dept_id = 4;

-- 第三步. 合并
select * from emp where dept_id = (select id from dept where name = '销售部');


-- 2. 查询在 "方东白" 入职之后的员工信息
-- 第一步. 查询 方东白 的入职日期
select entrydate from emp where name= '方东白';

-- 第二步. 查询他之后的员工信息
select * from emp where entrydate > '2009-02-12';

-- 第三步. 合并
select * from emp where entrydate > (select entrydate from emp where name= '方东白');

结果 :

2.列子查询

演示 : 

-- 列子查询
-- 1. 查询 "销售部" 和 "市场部" 的所有员工信息
-- a. 查询 "销售部" 和 "市场部" 的部门ID
select id from dept where name = '销售部' or name = '市场部';

-- b. 根据部门ID, 查询员工信息 ( in: 在指定范围内查找 )
select * from emp where dept_id in (2,4);

-- c. 合并
select * from emp where dept_id in (select id from dept where name = '销售部' or name = '市场部');

-- 2. 查询比 财务部 所有人工资都高的员工信息
-- a. 查询所有 财务部 人员工资
select id from dept where name = '财务部';

select salary from emp where dept_id = (select id from dept where name = '财务部');

-- b. 比 财务部 所有人工资都高的员工信息 (高于财务部最高工资的人, all : 查询返回的列表都需要满足条件)
select * from emp where salary > all (select salary from emp where dept_id = (select id from dept where name = '财务部'));


-- 3. 查询比研发部其中任意一人工资高的员工信息
-- a. 查询研发部所有人工资
select id from dept where name = '研发部';

select salary from emp where dept_id = (select id from dept where name = '研发部');

-- b. 比研发部其中任意一人工资高的员工信息 ( any/some : 查询满足其中的任意一个条件)
select * from emp where salary > any (select salary from emp where dept_id = (select id from dept where name = '研发部'));

结果 :

 

3.行子查询

演示 : 

-- 行子查询
-- 1. 查询与 "张无忌" 的薪资及直属领导相同的员工信息 ;
-- a. 查询 "张无忌" 的薪资及直属领导
select salary,managerid from emp where name = '张无忌';

-- b. 查询与 "张无忌" 的薪资及直属领导相同的员工信息 ;
select * from emp where (salary,managerid) = (select salary,managerid from emp where name = '张无忌');

结果 : 

4.表子查询

演示 :

-- 表子查询
-- 1. 查询与 "鹿杖客" , "宋远桥" 的职位和薪资相同的员工信息
-- a. 查询 "鹿杖客" , "宋远桥" 的职位和薪资
select job,salary from emp where name='鹿杖客' or name='宋远桥';

-- b. 查询与 "鹿杖客" , "宋远桥" 的职位和薪资相同的员工信息
select * from emp where (job,salary) in (select job,salary from emp where name='鹿杖客' or name='宋远桥');

-- 2. 查询入职日期是 "2006-01-01" 之后的员工信息 , 及其部门信息
-- a. 入职日期是 "2006-01-01" 之后的员工信息
select * from emp where entrydate > '2006-01-01';

-- b. 查询这部分员工, 对应的部门信息;
select e.*,d.* from (select * from emp where entrydate > '2006-01-01') e left join dept d on e.dept_id = d.id;

结果 :

 


 9.多表查询案例练习

演示 :

-- ---------------------------------------> 多表查询案例 <----------------------------------
create table salgrade(
    grade int,
    losal int,
    hisal int
) ***ment '薪资等级表';

insert into salgrade values (1,0,3000);
insert into salgrade values (2,3001,5000);
insert into salgrade values (3,5001,8000);
insert into salgrade values (4,8001,10000);
insert into salgrade values (5,10001,15000);
insert into salgrade values (6,15001,20000);
insert into salgrade values (7,20001,25000);
insert into salgrade values (8,25001,30000);

-- 1. 查询员工的姓名、年龄、职位、部门信息 (隐式内连接)
-- 表: emp , dept
-- 连接条件: emp.dept_id = dept.id

select e.name , e.age , e.job , d.name from emp e,dept d where e.dept_id = d.id;


-- 2. 查询年龄小于30岁的员工的姓名、年龄、职位、部门信息(显式内连接)
-- 表: emp , dept
-- 连接条件: emp.dept_id = dept.id

select e.name , e.age , e.job , d.name from emp e join dept d on e.dept_id = d.id where age < 30;


-- 3. 查询 拥有 员工的部门ID、部门名称 (人事部没有员工)
-- 表: emp , dept
-- 连接条件: emp.dept_id = dept.id  distinct关键字去重
select distinct d.id , d.name from emp e,dept d where e.dept_id = d.id;


-- 4. 查询所有年龄大于40岁的员工, 及其归属的部门名称; 如果员工没有分配部门, 也需要展示出来
-- 表: emp , dept
-- 连接条件: emp.dept_id = dept.id
-- 外连接
select e.* , d.name from emp e left join dept d on d.id = e.dept_id where age > 40;


-- 5. 查询所有员工的工资等级
-- 表: emp , salgrade
-- 连接条件 : emp.salary >= salgrade.losal and emp.salary <= salgrade.hisal
select e.* , s.grade , s.losal , s.hisal  from emp e , salgrade s where e.salary >= s.losal and e.salary <= s.hisal;
-- between...and 写法
select e.* , s.grade , s.losal, s.hisal from emp e , salgrade s where e.salary between s.losal and s.hisal;


-- 6. 查询 "研发部" 所有员工的信息及 工资等级
-- 表: emp , salgrade , dept
-- 连接条件 : emp.salary between salgrade.losal and salgrade.hisal , emp.dept_id = dept.id
-- 查询条件 : dept.name = '研发部'
select e.*, s.grade
from emp e,
     dept d,
     salgrade s
where e.dept_id = d.id
  and (e.salary between s.losal and s.hisal)
  and d.name = '研发部';


-- 7. 查询 "研发部" 员工的平均工资
-- 表: emp , dept
-- 连接条件 :  emp.dept_id = dept.id
select avg(e.salary) from emp e , dept d where e.dept_id = d.id and d.name = '研发部';


-- 8. 查询工资比 "灭绝" 高的员工信息。
-- a. 查询 "灭绝" 的薪资
select salary from emp where name = '灭绝';

-- b. 查询比她工资高的员工数据
select * from emp where salary > (select salary from emp where name = '灭绝');


-- 9. 查询比平均薪资高的员工信息
-- a. 查询员工的平均薪资
select avg(salary) from emp;

-- b. 查询比平均薪资高的员工信息
select * from emp where salary > (select avg(salary) from emp);


-- 10. 查询低于本部门平均工资的员工信息
-- a. 查询指定部门平均薪资  1
select avg(e1.salary) from emp e1 where dept_id = 1;
select avg(e1.salary) from emp e1 where dept_id = 2;

-- b. 查询低于本部门平均工资的员工信息
select *, (select avg(e1.salary) from emp e1 where dept_id = e2.dept_id) '平均'
from emp e2
where salary < (select avg(e1.salary) from emp e1 where dept_id = e2.dept_id);


-- 11. 查询所有的部门信息, 并统计部门的员工人数
select d.id , d.name , (select COUNT(*) from emp e where e.dept_id = d.id) '人数' from dept d;
select COUNT(*) from emp where dept_id = 1;


-- 12. 查询所有学生的选课情况, 展示出学生名称, 学号, 课程名称
-- 表: student , course , student_course
-- 连接条件: student.id = student_course.studentid , course.id = student_course.courseid
select s.name, s.no, c.name
from student s,
     student_course sc,
     course c
where s.id = sc.studentid
  and sc.courseid = c.id;

二、事务

1.事务简介


 2.事务操作 

操作方法 1 :

操作方法 2 :

 提交事务执行使用 ***mit ,如果执行过程中发生错误, 则 返回初始值 rollback.

 演示 :

-- ---------------------------- 事务操作 ----------------------------
-- 数据准备
create table a***ount(
    id int auto_increment primary key ***ment '主键ID',
    name varchar(10) ***ment '姓名',
    money int ***ment '余额'
) ***ment '账户表';
insert into a***ount(id, name, money) VALUES (null,'张三',2000),(null,'李四',2000);

-- 恢复数据
update a***ount set money = 2000 where name = '张三' or name = '李四';



select @@auto***mit; -- 查询当前事务执行方式, 查询结果 0 为手动提交 , 1 为自动提交

set @@auto***mit = 0; -- 设置为手动提交

-- 转账操作 (张三给李四转账1000)
-- 1. 查询张三账户余额
select * from a***ount where name = '张三';

-- 2. 将张三账户余额-1000
update a***ount set money = money - 1000 where name = '张三';

程序执行报错 ... -- 这里程序执行错误,前面两条语句执行,下面语句不会执行

-- 3. 将李四账户余额+1000
update a***ount set money = money + 1000 where name = '李四';

-- 事务执行成功,没有错误则 提交事务 手动提交
***mit;

-- 回滚事务 如果执行过程出错,则执行回滚操作,返回初始值
rollback ;



-- 方式二
-- 转账操作 (张三给李四转账1000)
start transaction ;  -- 开启事务

-- 1. 查询张三账户余额
select * from a***ount where name = '张三';

-- 2. 将张三账户余额-1000
update a***ount set money = money - 1000 where name = '张三';

程序执行报错 ... -- 这里程序执行错误,前面两条语句执行,下面语句不会执行

-- 3. 将李四账户余额+1000
update a***ount set money = money + 1000 where name = '李四';


-- 事务执行成功,没有错误则 提交事务 手动提交
***mit;

-- 回滚事务 如果执行过程出错,则执行回滚操作,返回初始值
rollback;

 3.事务四大特性

  •  原子性 : 要么事务执行成功要么就执行失败,进行回滚操作再进行下一次提交事务.
  •  一致性 : 比如例子中的转账操作,事务提交无论成功或失败,张三和李四账户的余额加起来是一个恒定的值,不会发生增加或减少.
  • 比如两个事务 A 和 B ,两个事务在操作过程中彼此之间不会影响之间的执行,两个事务是在独立的环境下运行.
  • 事务不管是操作成功提交了还是操作失败回滚了,它对数据库当中的数据改变是永久的,数据库当中的数据最终是存储在磁盘当中,所有数据就会永久的保留下来.

4.并发事务引发的问题

并发事务引起的问题 :多个并发执行事务在操作同一个数据库/表所引起的问题.

  • 脏读(没有执行完的事务 A 存储到磁盘后被事务 B 读取,则为脏读 ) : MySQL的隔离级别默认可以重复读取,也就是说未提交的数据根本读不到,这里只是告诉我们会有这种情况发生,而数据库隔离级别不会不会产生这种情况.
  •  不可重复读 : 事务 A 第一次执行查询一个 id=1 的语句后,再令 事务 B 更新了刚刚查询的语句 id=1 并且提交 ,如果 事务 A 再执行相同的查询 id=1 我们发现,此时查询出来的数据和第一次查询出来的数据不一样,这个现象就称为不可重复读.
  • 幻读(解决了不可重复读后引发出来的) : 

 5.事务隔离级别,解决事务并发问题

read un***mintted : (会出现脏读) 

如果相反设置为 set session transaction isolation level read ***mitted;后再次执行上面的操作,如果不进行 ***mit; 提交则数据不会发生改变.

read ***mintted : (会出现不可重复提交问题)

如果设置为 set session transaction isolation level repeatable read; 后再次 ***mit 提交,不可重复读问题就会被解决.

 repeatable read : (会出现幻读问题)

 要想解决幻读问题,则要设置事务的隔离级别为 set session transaction isolation level serializable;

 serializable (可以规避所有的并发事务问题,但是它的性能是最差的) : A 事务 ***mit 提交后, B 事务才能进行操作. 

注意 : 事务的隔离级别越高, 数据越安全, 但是性能越低 .


总结

多表查询 :

事务 :


码文不易,三联支持一下呗

如有不足,还望指出.

转载请说明出处内容投诉
CSS教程_站长资源网 » MySQL笔记-多表查询

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买