• 基础篇04——多表查询


    多表关系

    一对多

    多对多

    多对多是通过中间表实现的

    1. -- 创建学生表
    2. create table student
    3. (
    4. id int auto_increment primary key comment 'ID',
    5. name varchar(10) comment '姓名',
    6. no varchar(3) comment '学号'
    7. ) comment '学生表';
    8. insert into student
    9. values (null, '黛绮丝', '001'),
    10. (null, '谢逊', '002'),
    11. (null, '小明', '003'),
    12. (null, '小红', '004');
    13. -- 创建课程表
    14. create table course
    15. (
    16. id int auto_increment primary key comment 'ID',
    17. name varchar(10) comment '课程名称'
    18. ) comment '课程表';
    19. insert into course
    20. values (null, '语文'),
    21. (null, '数学'),
    22. (null, '英语');
    23. -- 创建中间表,维护学生表和课程表之间的关系
    24. create table student_course
    25. (
    26. id int auto_increment primary key comment 'ID',
    27. studentid int not null comment '学生ID',
    28. courseid int not null comment '课程ID',
    29. constraint fk_courseid foreign key (courseid) references course (id),
    30. constraint fk_studentid foreign key (studentid) references student (id)
    31. ) comment '学生课程中间表';
    32. insert into student_course
    33. values (null, 1, 1),
    34. (null, 1, 2),
    35. (null, 1, 3),
    36. (null, 2, 2),
    37. (null, 2, 3),
    38. (null, 3, 3);

    一对一 

    1. -- 创建用户表
    2. create table tb_user
    3. (
    4. id int auto_increment primary key comment 'ID',
    5. name varchar(10) comment '姓名',
    6. age tinyint unsigned comment '年龄',
    7. gender char(1) comment '性别',
    8. phone char(11) comment '手机号'
    9. ) comment '用户基本信息表';
    10. -- 创建用户教育信息表
    11. create table tb_user_edu
    12. (
    13. id int auto_increment primary key comment 'ID',
    14. degree varchar(20) comment '学历',
    15. major varchar(50) comment '专业',
    16. primaryschool varchar(50) comment '小学',
    17. middleschool varchar(50) comment '中学',
    18. university varchar(50) comment '大学',
    19. userid int unique comment '用户ID',
    20. constraint fk_userid foreign key (userid) references tb_user (id)
    21. ) comment '用户教育信息表';
    22. insert into tb_user(id, name, age, gender, phone)
    23. values (null, '小明', 16, '1', '10000000001'),
    24. (null, '小花', 13, '2', '10000000002'),
    25. (null, '小华', 15, '1', '10000000003'),
    26. (null, '小红', 14, '2', '10000000004');
    27. insert into tb_user_edu(id, degree, major, primaryschool, middleschool, university, userid)
    28. values (null, '本科', '舞蹈', 'XXX小学', 'XXX中学', 'XXX大学', 1),
    29. (null, '大专', '会计', 'YYY小学', 'YYY中学', 'YYY大学', 2),
    30. (null, '硕士', '英语', 'AAA小学', 'AAA中学', 'AAA大学', 3),
    31. (null, '博士', '临床医学', 'BBB小学', 'BBB中学', 'BBB大学', 4);

    多表查询概述

    多表查询指的是从多张表中查询数据

    用以下例子举例

    笛卡尔积

    当直接查询两张表时(即执行命令:select * from emp,  dept;),查询的结果就是笛卡尔积,查询结果数为两张表的数据量相乘,我们需要消除无效的笛卡尔积,如下

    1. select *
    2. from emp,
    3. dept
    4. where emp.dept_id = dept.id;

     多表查询分类

    内连接

    1. -- 隐式内连接实现:查询每一个员工姓名及其所属部门名称
    2. select emp.name, dept.name
    3. from emp,
    4. dept
    5. where dept_id = dept.id;
    6. -- 显式内连接实现:查询每一个员工姓名及其所属部门名称
    7. -- 可以给表起别名来简化代码,但是起别名之后不能再用表原来的名称
    8. -- inner关键字可以省略
    9. select e.name, d.name
    10. from emp e
    11. inner join dept d on e.dept_id = d.id;

    外连接

    左外连接和右外连接可以相互转换,即左外连接可以用右外连接替代,右外连接也可以用左外连接替代,只需要更改关键字left、right和两个表的位置即可,这里就不展示了。 

    1. -- 查询emp表的所有数据,和对应的部门信息(左外连接)
    2. -- outer 关键字可以省略
    3. -- from 后跟着的表为左表,join后跟着的表为右表
    4. -- 两个表的顺序不一样查询结果也不一样
    5. -- 左外连接表示展示左表的所有数据,以及左表中每一条数据对应的右表数据,如果右表没有数据则显示为空
    6. -- 右外连接则反过来,表示展示右表的所有数据,以及右表中每一条数据对应的左表数据,如果左表没有数据则显示为空
    7. -- 自己运行一下代码就可以明白
    8. select e.*, d.name -- e.* 表示查询emp表中的所有数据
    9. from emp e
    10. left outer join dept d
    11. on e.dept_id = d.id;
    12. -- 查询所有的部门信息,和对应的emp表数据(右外连接(
    13. select d.*, e.*
    14. from emp e
    15. right outer join dept d on d.id = e.dept_id;

    自连接

    自连接的外连接查询可以是左外也可以是右外。

    1. -- 查询所有员工的名字及其所属领导的名字
    2. -- 显式内连接实现
    3. select e1.name as '员工姓名', e2.name as '领导姓名'
    4. from emp e1
    5. join emp e2 on e1.managerid = e2.id;
    6. -- 隐式内连接实现
    7. select e1.name '员工姓名', e2.name '领导姓名'
    8. from emp e1,
    9. emp e2
    10. where e1.managerid = e2.id;
    11. -- 查询所有员工的名字及其所属领导的名字,如果员工没有领导也要查询出来
    12. -- 这里用外连接实现,用的是左外连接
    13. select e1.name '员工姓名', e2.name '领导姓名'
    14. from emp e1
    15. left join emp e2 on e1.managerid = e2.id;

    联合查询union

    1. -- 将薪资低于5000的员工和年龄大于40的员工全部查询出来
    2. -- 即员工满足两个条件之一就需要被查询出来
    3. -- 有 all 关键字查询结果不去重,没有 all 则会将查询结果去重
    4. -- 只有 select 和 from 之间的字段列表的列数和类型一致时才可以用联合查询
    5. select *
    6. from emp
    7. where salary < 5000
    8. union all
    9. select *
    10. from emp
    11. where age > 40;

    子查询

    子查询一般都用小括号括起来,可以放在 select、from、where 这几个位置(具体看后面案例)

    标量子查询

    1. -- 查询研发部的所有员工信息
    2. select *
    3. from emp
    4. where dept_id = (select id from dept where dept.name = '研发部');

    列子查询

    1. -- 查询销售部和市场部的所有员工信息
    2. select *
    3. from emp
    4. where dept_id in (select dept.id
    5. from dept
    6. where dept.name in ('销售部', '市场部'));
    7. -- 查询比财务部 所有人 工资都高的员工信息
    8. -- 查询财务部的id:select id from dept where dept.name = '财务部'
    9. -- 先查出财务部员工最高的工资
    10. -- select max(salary) from emp where dept_id = (select id from dept where dept.name = '研发部');
    11. -- 实现方式1:
    12. select *
    13. from emp
    14. where salary > (select max(salary)
    15. from emp
    16. where dept_id = (select id
    17. from dept
    18. where dept.name = '财务部'));
    19. -- 实现方式2:(all)
    20. select *
    21. from emp
    22. where salary > all (select salary
    23. from emp
    24. where dept_id = (select id
    25. from dept
    26. where dept.name = '财务部'));
    27. -- 查询比研发部 任意一人 工资高的员工信息(any/some)
    28. select *
    29. from emp
    30. where salary > any (select salary
    31. from emp
    32. where dept_id = (select id
    33. from dept
    34. where dept.name = '研发部'));

    行子查询

    1. -- 查询与“张无忌”薪资及直属领导相同的员工信息
    2. -- 先查出张无忌的薪资和指数领导id
    3. # select salary, managerid from emp where name='张无忌';
    4. # 假设查出结果为(10000, 1)
    5. # (salary, managerid) = ( select ...) => salary=10000 and managerid=1
    6. select *
    7. from emp
    8. where (salary, managerid) = (select salary, managerid from emp where name = '张无忌');

    表子查询

    1. -- 查询与 小明,小红 的职位和薪资相同的员工信息
    2. # 1、查询小明,小红 的职位和薪资
    3. select job, salary
    4. from emp
    5. where name in ('小明' '小红');
    6. select *
    7. from emp
    8. where (job, salary) in (select job, salary
    9. from emp
    10. where name in ('小明' '小红'));
    11. -- 查询入职日期是'2018-01-01'之后的员工信息及其部门信息
    12. # 查询入职日期是'2006-01-01'之后的员工id
    13. select id
    14. from emp
    15. where entrydate > '2006-01-01';
    16. # (select * from emp where entrydate > '2006-01-01') 查询结果作为一张临时表
    17. # 给临时表起别名为 e
    18. select e.*, d.name
    19. from (select * from emp where entrydate > '2006-01-01') e
    20. left join dept d on e.dept_id = d.id;

    多表查询案例练习

    上面的例子涉及之前所说的emp员工表、dept部门表和薪资等级表,薪资等级表的表结构如下:

    三张表的结构如下:

    1. -- 例题1(隐式内连接)
    2. select e.name, e.age, e.job, d.name
    3. from emp e,
    4. dept d
    5. where e.dept_id = d.id;
    6. -- 例题2(显式内连接)
    7. select e.name, e.age, e.job, d.name
    8. from emp e
    9. inner join dept d on e.dept_id = d.id
    10. where e.age < 30;
    11. -- 例题3(结果要去重)
    12. select distinct d.id, d.name
    13. from dept d
    14. inner join emp e on d.id = e.dept_id;
    15. -- 例题4(左外连接)
    16. select e.*, d.name
    17. from emp e
    18. left outer join dept d on e.dept_id = d.id
    19. where e.age > 40;
    20. -- 例题5
    21. # 隐式内连接
    22. select e.name, sg.grade
    23. from salgrade sg,
    24. emp e
    25. where e.salary between sg.losal and sg.hisal;
    26. # 显式内连接
    27. select e.name, sg.grade
    28. from salgrade sg
    29. inner join emp e on e.salary between sg.losal and sg.hisal;
    30. -- 例题6
    31. select e.*, sg.grade
    32. from emp e,
    33. salgrade sg
    34. where e.dept_id = (select id from dept d where d.name = '研发部')
    35. and e.salary between sg.losal and sg.hisal;
    36. -- 例题7
    37. # 方式一
    38. select avg(e.salary)
    39. from emp e
    40. where e.dept_id = (select d.id from dept d where d.name = '研发部');
    41. # 方式二
    42. select avg(e.salary)
    43. from emp e,
    44. dept d
    45. where e.dept_id = d.id
    46. and d.name = '研发部';
    47. -- 例题8
    48. select *
    49. from emp
    50. where salary > (select salary from emp where name = '灭绝');
    51. -- 例题9
    52. select *
    53. from emp
    54. where salary > (select avg(salary) from emp);
    55. -- 例题10(放在where位置的子查询)
    56. select e1.*
    57. from emp e1
    58. where e1.salary < (select avg(e2.salary)
    59. from emp e2
    60. where e1.dept_id = e2.dept_id);
    61. -- 例题11(放在select位置的子查询)
    62. select d.id,
    63. d.name,
    64. (select count(e.id)
    65. from emp e
    66. where e.dept_id = d.id) '部门员工数量'
    67. from dept d;
    68. -- 例题12
    69. select s.name, s.no, c.name
    70. from student_course sc,
    71. student s,
    72. course c
    73. where sc.courseid = c.id
    74. and sc.studentid = s.id;

  • 相关阅读:
    Linux Centos 安装最新Redis6.2.4版本
    Vue2监听浏览器可视区域的大小,
    Leetcode-1653. 使字符串平衡的最少删除次数
    【算法刷题日记之本手篇】完全数计算与扑克牌大小
    Meta-World:多任务、持续学习、终身学习、元学习、强化学习的基准和评估
    y92.第六章 微服务、服务网格及Envoy实战 -- Envoy基础(三)
    微突发丢包的艺术
    WebSocket ----苍穹外卖day8
    RabbitMq中的warren模式和shovel模式
    计算机网络与技术——概述
  • 原文地址:https://blog.csdn.net/2301_77659011/article/details/139376720