MySQL多表查询

一、准备数据

先要准备两张表,一张员工表(emp),一张部门表。

建表sql

sql 复制代码
-- 准备数据
create table dept(
    id   int auto_increment comment 'ID' primary key,
    name varchar(50) not null comment '部门名称'
)comment '部门表';

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

-- 添加外键
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);


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

二. 多表关系概述

  1. 一对多

常见案例 :用户和部门。在通常情况下,在多的一方表里建立外键来进行关联。一个员工对应一个部门,一个部门可以有多个员工。

  1. 多对多

常见案例,学生和课程。一个学生可以选修多个课程,一个课程也可以有多个学生。通常多对多的情况下,会建立一张中间表,分别带有两方主键作为外键来建立关系。

  1. 一对一

常见案例,用户和用户详情。多用于单表拆分,将一张表的基础信息放在一张表中,其余详情信息放在另一张表中,以提高操作效率。通常在任意一方加入外键,关联另一方的主键。且外键设为唯一(UNIQUE)

三、多表查询

  1. 内连接

内连接查询的是两张表的交集。分为显示内连接和隐式内连接。

sql 复制代码
-- 内连接演示

-- 1.查询每一个员工的姓名及对应的部门名称(隐式内连接)
-- 表结构: emp,dept
-- 连接关系: emp.dept_id = emp.id
select emp.name,dept.name
from emp,dept where dept.id = emp.dept_id;

-- 2.查询每一个员工的姓名及对应的部门名称(显式内连接)
-- 表结构: emp,dept
-- 连接关系: emp.dept_id = emp.id
select e.name,d.name from emp e inner join dept d on e.dept_id = d.id;
  1. 外连接

外连接包含左连接和右连接,左连接相当于查询左表的所有数据并包含左右表交集部分的数据。右连接则相当于查询右表的所有数据并包含左右表交集部分的数据。

sql 复制代码
-- 外连接
-- 左外连接,查询左表的所有数据,并输出以之相匹配的右表数据
select e.name,d.name from emp e left join dept d on d.id = e.dept_id;
-- 右外连接,查询右表的所有数据,并输出以之相匹配的表表数据
select e.name,d.name from emp e right join dept d on d.id = e.dept_id;
  1. 自连接

顾名思义,内连接就是同一张表自己连接自己。可以是左连接或者右连接。需要用别名做区分。

这里直接看两个需求:

sql 复制代码
-- 自连接 通过内连接,左连接,右连接都可以
-- 查询所有员工姓名且以之相对应的领导姓名(不包含没有领导的数据)
select e1.name as emp,e2.name as manager from emp e1 inner join emp e2 on e1.managerid = e2.id;
-- 查询所有员工姓名且以之相对应的领导姓名(包含没有领导的数据)
select e1.name as emp,e2.name as manager from emp e1 left join emp e2 on e1.managerid = e2.id;

第一张表e1当做是查询所有员工,第二张表e2可以当成查询领导。则关联关系为e1.id = e2.managerid。e1.name就是员工名,e2.name就是领导名称。至于包含不包含没有领导的数据,则就是外连接和内连接的区别了。

  1. 子查询

子查询分为三种情况:标量子查询、列子查询、行子查询和表子查询

标量子查询,即返回的结果只有单个值,常见的操作符 = ,<>, > ,<,<=,>=

sql 复制代码
-- 标量子查询
-- 查询部门为销售部的员工
select * from emp left join dept on emp.dept_id = dept.id where dept.name = '销售部';
select * from emp where dept_id = (select id from dept where name = '销售部');

列子查询,即返回的结果为一列(可以是多行)

sql 复制代码
-- 列子查询(可以是多行)
-- 查询销售部和市场部的员工信息
select emp.* from emp left join dept d on emp.dept_id = d.id where d.name = '销售部' or d.name = '市场部';
select * from emp where emp.dept_id in (select id from dept where dept.name = '销售部' or dept.name = '市场部');

-- 查询财务部比所有人工资都高的员工信息
select * from emp where salary > all (select salary from emp where dept_id = (select id from dept where dept.name = '财务部'));

-- 查询比研发部其中一人工资高的员工信息
select * from emp where salary > any (select salary from emp where dept_id = (select id from dept where dept.name = '研发部'));

行子查询,即返回的结果为一行,这种查询称为行子查询(多个属性可以用,隔开)

sql 复制代码
-- 行子查询
-- 查询张无忌的薪资及其领导相同的员工信息
select * from emp where (salary,managerid) = (
    select e1.salary,e1.managerid from emp e1 where e1.name = '张无忌'
    );

select e1.* from emp e1
right join (select e1.salary,e1.managerid from emp e1 where e1.name = '张无忌') a on a.managerid = e1.managerid and a.salary = e1.salary;

表子查询,即返回的结果多行多列,这种查询称为行表查询(通常情况下可以进行关联)

sql 复制代码
-- 表子查询

-- 查询'鹿杖客'与'宋远桥'的职位和薪资相同的员工信息
select * from emp right join
(
    select job,salary
    from emp where name = '鹿杖客' or name = '宋远桥'
) a on a.salary = emp.salary and emp.job = a.job
where name != '宋远桥' and name <> '鹿杖客';

select * from emp where (salary,job) in (select salary,job from emp  where name = '鹿杖客' or name = '宋远桥' )
and name != '宋远桥' and name <> '鹿杖客';

-- 查询入职日期是'2006-01-01'之后的员工信息,及其部门信息
select emp.name,dept.name from emp left join dept on emp.dept_id = dept.id where emp.entrydate > '2006-01-01';

select a.name,dept.name
from dept right join
(
  select dept_id,name from emp where entrydate > '2006-01-01'
) a on a.dept_id = dept.id;

四、练习

练习需要再建立一张工资等级表

sql 复制代码
create table salgrade(
    grade int,
    losal int,
    hisal int
) comment '薪资等级表';

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);
sql 复制代码
-- 查询员工的姓名、年龄、职位、部门信息
select emp.name,emp.age,emp.job,dept.name from emp,dept where emp.dept_id = dept.id;
select emp.name,emp.age,emp.job,d.name from emp left join dept d on d.id = emp.dept_id;

-- 查询年龄小于30岁的员工姓名、年龄、职位、部门信息
select emp.name,emp.age,emp.job,d.name from emp left join dept d on d.id = emp.dept_id where emp.age < 30;

-- 查询拥有员工的部门ID、部门名称
select emp.name,emp.dept_id,d.name from emp left join dept d on d.id = emp.dept_id order by d.id;

-- 查询所有年龄大于40岁的员工,及其归属的部门名称;如果员工没有分配部门,也要显示出来
select emp.name,d.name from emp left join dept d on d.id = emp.dept_id where emp.age > 40;

-- 查询所有员工的工资等级
select e.name,e.salary,s.grade from salgrade s,emp e where e.salary >= s.losal and e.salary <= s.hisal;

-- 查询'研发部'所有员工的信息及工资等级
select
    emp.name,
    case
        when salary > 20000 then 'A'
        when 10000<emp.salary and salary <=20000 then 'B'
        when 5000<emp.salary and salary <=10000 then 'C'
        when 2000<emp.salary and salary <=5000 then 'D'
        else '乞丐' end as level
from emp left join dept d on d.id = emp.dept_id where d.name = '研发部' order by salary;

-- 查询研发部员工的平均工资
select avg(salary) from emp left join dept d on d.id = emp.dept_id where d.name = '研发部';

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

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

-- 查询低于本部门平均工资的员工信息
select emp.name,emp.salary,s.name from emp
left join
    (
        select d.id,d.name,avg(salary) as salary from emp left join dept d on d.id = emp.dept_id group by d.id,d.name
    ) s on s.id = emp.dept_id
where emp.salary < s.salary;

-- 查询所有部门信息,并统计部门的员工人数
select dept.id,dept.name,count(e.id) from dept left join emp e on dept.id = e.dept_id group by dept.name,dept.id order by dept.id;

-- 查询所有学生的选课情况,展示出学生名称,学号,课程名称
相关推荐
云和数据.ChenGuang14 分钟前
Django 应用安装脚本 – 如何将应用添加到 INSTALLED_APPS 设置中 原创
数据库·django·sqlite
woshilys42 分钟前
sql server 查询对象的修改时间
运维·数据库·sqlserver
Hacker_LaoYi43 分钟前
SQL注入的那些面试题总结
数据库·sql
建投数据2 小时前
建投数据与腾讯云数据库TDSQL完成产品兼容性互认证
数据库·腾讯云
Hacker_LaoYi3 小时前
【渗透技术总结】SQL手工注入总结
数据库·sql
岁月变迁呀3 小时前
Redis梳理
数据库·redis·缓存
独行soc3 小时前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍06-基于子查询的SQL注入(Subquery-Based SQL Injection)
数据库·sql·安全·web安全·漏洞挖掘·hw
你的微笑,乱了夏天3 小时前
linux centos 7 安装 mongodb7
数据库·mongodb
工业甲酰苯胺3 小时前
分布式系统架构:服务容错
数据库·架构
独行soc4 小时前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍08-基于时间延迟的SQL注入(Time-Based SQL Injection)
数据库·sql·安全·渗透测试·漏洞挖掘