MySQL 多表的关系


MySQL 多表的关系

1. 多表查询

  • 同时查询多张表获取到需要的数据组成完整的信息返回给用户。

a. 准备数据

-- 多表查询
create database regino;
use regino;
-- 创建部门表(主表)
CREATE TABLE dept (
  id INT PRIMARY KEY AUTO_INCREMENT,
  NAME VARCHAR(20)
);

INSERT INTO dept (NAME) VALUES ('开发部'),('市场部'),('财务部'),('销售部');

-- 创建员工表(从表)
CREATE TABLE emp (
  id INT PRIMARY KEY AUTO_INCREMENT,
  NAME VARCHAR(10),
  gender CHAR(1), -- 性别(sex)
  salary DOUBLE, -- 工资
  join_date DATE, -- 入职日期
  dept_id INT -- 外键字段
);

INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('孙悟空','男',7200,'2013-02-24',1);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('猪八戒','男',3600,'2010-12-02',2);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('唐僧','男',9000,'2008-08-08',2);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('白骨精','女',5000,'2015-10-07',3);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('蜘蛛精','女',4500,'2011-03-14',1);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('沙僧','男',6666,'2017-03-04',null);

b. 关系图

在这里插入图片描述

c. 笛卡尔积(Cartesian product)

  • A × B = { ( x , y ) ∣ x ∈ A ∧ y ∈ B } A×B = \left\{{(x,y)|x∈A∧y∈B}\right\} A×B={(x,y)xAyB}
  • 功能:多张表的记录进行组合,这种现象称为笛卡尔积(交叉连接)。将两张表每一条记录组合,表 A 记录个数 * 表 B 记录个数 = 笛卡尔积个数。
  • 语法:select ... from 左表,右表;
  • 示例:
-- 查询二张表
SELECT * FROM emp,dept;
SELECT COUNT(*) FROM emp,dept;

在这里插入图片描述


d. 如何消除笛卡尔积现象?

  • 从表的外键列 = 主表的主键列
  • 连接条件为 表个数 - 1
SELECT * FROM emp e INNER JOIN dept d ON e.`dept_id` = d.`id`;
  • 注意:不能用 dept_id = id,因为这样写是有歧义的,比如两个表都有 id。这里是固定语法,把表的别名当成对象,可以调用该表的成员也就是列名。
  • 条件生效顺序:on > where > 聚合函数 > having

2. 内连接与外链接

在这里插入图片描述

a. 内连接

i. 语法
  • 功能:拿左表的记录去匹配由标的记录,若符合,条件显示(二张表的交集,即符合条件才会显示)。
  • 语法:
    • 隐式内连接:
      select ... from 左表,右表 where 连接条件;
      连接条件一般是 左表.id = 右表.id
    • 【推荐】显示内连接:
      select ... from 左表 [inner] join 右表 on 连接条件;
  • 这两个效果是一样的:
    在这里插入图片描述
  • 为什么说 where 是隐式,而 inner join on 是显示呢?
    首先,这个单词的意思翻译过来就是内连接,where 并没显示是连接查询,然而有关联条件的约束,也能达到连接查询的效果,所以 where 是隐式内连接。
ii. 示例
-- 内连接
-- 隐式内连接
SELECT * FROM emp e,dept d WHERE e.dept_id = d.id;

-- 显示内连接
SELECT * FROM emp e INNER JOIN dept d ON e.dept_id = d.id;

-- 查询唐僧的 id,姓名,性别,工资和所在部门名称
-- 1.确定查询表
SELECT * FROM emp e INNER JOIN dept d;
-- 2.确定连接条件
SELECT * FROM emp e INNER JOIN dept d ON e.dept_id = d.id;
-- 3.确定显示字段
SELECT e.id,e.name,e.gender,e.salary,d.name FROM emp e INNER JOIN dept d ON e.dept_id = d.id;
-- 4.确定业务条件
SELECT e.id,e.name,e.gender,e.salary,d.name FROM emp e INNER JOIN dept d ON e.dept_id = d.id WHERE e.name = '唐僧';

b. 外连接

i. 语法
  1. 【推荐】左外连接:
    • 功能:展示左表全部,再去匹配右表记录,右表的记录只有符合条件才会显示。左表中,若条件符合则显示,若条件不符合则显示 NULL。
    • 语法:select ... from 左表 left [outer] join 右表 on 连接条件;
  2. 右外连接:
    • 功能:展示右表全部,再去匹配左表记录,左表的记录只有符合条件才会显示。右表中,若条件符合则显示,若条件不符合则显示 NULL。
    • 语法:select ...from 左表 right [outer] join 右表 on 连接条件;
ii. 示例
# 左外连接(推荐)
-- 查询所有员工信息及对应的部门名称
SELECT * FROM emp e LEFT OUTER JOIN dept d ON e.dept_id = d.id;
-- 查询所有部门及对应的员工信息
SELECT * FROM dept d LEFT JOIN emp e ON e.dept_id = d.id;

# 右外连接
-- 查询所有部门及对应的员工信息
SELECT * FROM emp e RIGHT OUTER JOIN dept d ON e.dept_id = d.id;
  • 连接关系左右互换有影响吗?
    有影响。selecet * from A left outer join B on 连接条件。A 表在左边,A 表为左表,A 表数据全部显示;B 表为右表,符合条件才显示。如果调换 A、B 两个表的顺序,那么 B 表则为左表了。

3. 子查询(嵌套)

a. 语法

  • 功能:一条 select 语句执行结果,作为另一条 select 语法的一部分
  • 子查询语法:
    • 查询结果单值:
      SELECT MAX(salary) FROM emp;
    • 查询结果单列多值:
      SELECT salary FROM emp;
    • 查询结果多列多值:
      SELECT * FROM emp;
  • 语法及规律:
    • 子查询结果为单列,肯定作为条件在 where 后面使用:
      select ... from 表名 where 字段 in (子查询);
    • 子查询结果为多列,一般作为虚拟表(又称临时表)在 from 后面使用:
      select ... from (子查询) as 表别名;

b. 示例

# 子查询

# 子查询结果为单值
-- 1 查询工资最高的员工是谁? 
SELECT * FROM emp WHERE salary = (SELECT MAX(salary) FROM emp);
-- 2 查询工资小于平均工资的员工有哪些?
-- 2.1 先求出平均工资
SELECT AVG(salary) FROM emp;
-- 2.2 查询低于平均工资的员工
SELECT * FROM emp WHERE salary < (SELECT AVG(salary) FROM emp);

# 子查询结果为单列多行
-- 1 查询工资大于5000的员工,来自于哪些部门的名字  
-- 1.1 查询工资大于5000的员工
SELECT dept_id FROM emp WHERE salary >5000;
-- 1.2 来自于哪些部门的名字  
SELECT * FROM dept WHERE id IN(SELECT dept_id FROM emp WHERE salary >5000);


-- 2 查询开发部与财务部所有的员工信息
-- 2.1 根据部门名称,查询部门主键
SELECT id FROM dept WHERE `name` IN('开发部','财务部');
-- 2.2 根据部门id查询员工信息
SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE `name` IN('开发部','财务部'));


# 子查询结果为多列多行
-- 1 查询出`dept`,包括部门名称

-- 方案一
-- 1.1 查询出2011年以后入职的员工信息
SELECT * FROM emp WHERE join_date > '2011-1-1';
-- 1.2 通过临时表跟部门表关联
SELECT * FROM (SELECT * FROM emp WHERE join_date > '2011-1-1') e LEFT  JOIN dept d ON e.dept_id = d.id;


-- 方案二
-- 1.1 先实现二张表关联
SELECT * FROM emp e LEFT OUTER JOIN dept d ON e.dept_id = d.id;
-- 1.2 再过滤2011年以后入职的
SELECT * FROM emp e LEFT OUTER JOIN dept d ON e.dept_id = d.id WHERE e.join_date > '2011-1-1';

4. 综合案例

  • 在企业开发时,根据不同的业务需求往往需要通过 2 张及以上的表中去查询需要的数据。其实不管是几张表的查询,都是有规律可循的。

a. 准备数据

-- 多表案例
create database regino;
use regino;
-- 部门表
CREATE TABLE dept (
  id INT PRIMARY KEY auto_increment, -- 部门id
  dname VARCHAR(50), -- 部门名称
  loc VARCHAR(50) -- 部门位置
);

-- 添加4个部门
INSERT INTO dept(id,dname,loc) VALUES 
(10,'教研部','北京'),
(20,'学工部','上海'),
(30,'销售部','广州'),
(40,'财务部','深圳');

-- 职务表
CREATE TABLE job (
  id INT PRIMARY KEY,
  jname VARCHAR(20), -- 职务名称
  description VARCHAR(50) -- 职务描述
);

-- 添加4个职务
INSERT INTO job (id, jname, description) VALUES
(1, '董事长', '管理整个公司,接单'),
(2, '经理', '管理部门员工'),
(3, '销售员', '向客人推销产品'),
(4, '文员', '使用办公软件');

-- 员工表
CREATE TABLE emp (
  id INT PRIMARY KEY, -- 员工id
  ename VARCHAR(50), -- 员工姓名
  job_id INT, -- 职务id  外键
  mgr INT , -- 上级领导
  joindate DATE, -- 入职日期
  salary DECIMAL(7,2), -- 工资 99999.99
  bonus DECIMAL(7,2), -- 奖金 99999.99
  dept_id INT, -- 所在部门编号  外键
  CONSTRAINT emp_jobid_ref_job_id_fk FOREIGN KEY (job_id) REFERENCES job (id),
  CONSTRAINT emp_deptid_ref_dept_id_fk FOREIGN KEY (dept_id) REFERENCES dept (id)
);

-- 添加员工
INSERT INTO emp(id,ename,job_id,mgr,joindate,salary,bonus,dept_id) VALUES 
(1001,'孙悟空',4,1004,'2000-12-17','8000.00',NULL,20),
(1002,'卢俊义',3,1006,'2001-02-20','16000.00','3000.00',30),
(1003,'林冲',3,1006,'2001-02-22','12500.00','5000.00',30),
(1004,'唐僧',2,1009,'2001-04-02','29750.00',NULL,20),
(1005,'李逵',4,1006,'2001-09-28','12500.00','14000.00',30),
(1006,'宋江',2,1009,'2001-05-01','28500.00',NULL,30),
(1007,'刘备',2,1009,'2001-09-01','24500.00',NULL,10),
(1008,'猪八戒',4,1004,'2007-04-19','30000.00',NULL,20),
(1009,'罗贯中',1,NULL,'2001-11-17','50000.00',NULL,10),
(1010,'吴用',3,1006,'2001-09-08','15000.00','0.00',30),
(1011,'沙僧',4,1004,'2007-05-23','11000.00',NULL,20),
(1012,'李逵',4,1006,'2001-12-03','9500.00',NULL,30),
(1013,'小白龙',4,1004,'2001-12-03','30000.00',NULL,20),
(1014,'关羽',4,1007,'2002-01-23','13000.00',NULL,10);

-- 工资等级表
CREATE TABLE salarygrade (
  grade INT PRIMARY KEY,  -- 等级
  losalary INT, -- 最低工资
  hisalary INT -- 最高工资
);

-- 添加5个工资等级
INSERT INTO salarygrade(grade,losalary,hisalary) VALUES 
(1,7000,12000),
(2,12010,14000),
(3,14010,20000),
(4,20010,30000),
(5,30010,99990);
> 关系图

在这里插入图片描述

b. 外键约束

-- 1 查询所有员工信息。显示员工编号,员工姓名,工资,职务名称,职务描述
-- 1.1 确定几张表?
SELECT * FROM emp e INNER JOIN job j;
-- 1.2 确定连接条件
SELECT * FROM emp e INNER JOIN job j ON e.job_id = j.id;
-- 1.3 确定显示字段(列)
SELECT e.id,e.ename,e.salary,j.jname,j.description FROM emp e INNER JOIN job j ON e.job_id = j.id;
-- 2 查询所有员工信息。显示员工编号,员工姓名,工资,职务名称,职务描述,部门名称,部门位置
-- 2.1 确定几张表?
SELECT * FROM emp e 
	INNER JOIN job j 
	INNER JOIN dept d;
	
-- 2.2 确定连接条件
SELECT * FROM emp e 
	INNER JOIN job j ON e.job_id = j.id
	INNER JOIN dept d ON e.dept_id = d.id;
	
-- 2.3 确定显示字段
SELECT e.id,e.ename,e.salary,j.jname,j.description,d.dname,d.loc FROM emp e 
	INNER JOIN job j ON e.job_id = j.id
	INNER JOIN dept d ON e.dept_id = d.id;
-- 3 查询所有员工信息。显示员工姓名,工资,职务名称,职务描述,部门名称,部门位置,工资等级
-- 3.1 确定几张表
SELECT * FROM emp e
	INNER JOIN job j
	INNER JOIN dept d
	INNER JOIN salarygrade sg;
	
-- 3.2 确定连接条件
SELECT * FROM emp e
	INNER JOIN job j ON e.job_id = j.id
	INNER JOIN dept d ON e.dept_id = d.id
	INNER JOIN salarygrade sg ON e.salary BETWEEN sg.losalary AND sg.hisalary;
	
-- 3.3 确定显示字段
SELECT e.ename,e.salary,j.jname,j.description,d.dname,d.loc,sg.grade FROM emp e
	INNER JOIN job j ON e.job_id = j.id
	INNER JOIN dept d ON e.dept_id = d.id
	INNER JOIN salarygrade sg ON e.salary BETWEEN sg.losalary AND sg.hisalary;
-- 4 查询经理的信息。显示员工姓名,工资,职务名称,职务描述,部门名称,部门位置,工资等级

-- 直接将第三题代码粘过来
SELECT e.ename,e.salary,j.jname,j.description,d.dname,d.loc,sg.grade FROM emp e
	INNER JOIN job j ON e.job_id = j.id
	INNER JOIN dept d ON e.dept_id = d.id
	INNER JOIN salarygrade sg ON e.salary BETWEEN sg.losalary AND sg.hisalary
	WHERE j.jname = '经理';
-- 5 查询出部门编号、部门名称、部门位置、部门人数 (重点)
-- 5.1 查询出部门编号、部门名称、部门位置
SELECT * FROM dept;
-- 5.2 部门人数(员工表:分组+聚合 )
SELECT dept_id,COUNT(*) AS total FROM emp GROUP BY dept_id;
-- 5.3 部门表左外关联临时表
SELECT d.id,d.dname,d.loc,e.total FROM dept d 
	LEFT JOIN (SELECT dept_id,COUNT(*) AS total FROM emp GROUP BY dept_id) e ON d.id = e.dept_id;
> 关系图

在这里插入图片描述

c. 左外链接

-- 6 查询每个员工的名称及其上级领导的名称(自关联)
SELECT 
  yuangong.id,
  yuangong.ename,
  lingdao.id,
  lingdao.ename 
FROM
  emp yuangong 
  LEFT OUTER JOIN emp lingdao 
    ON yuangong.mgr = lingdao.id ;
> 关系图

在这里插入图片描述


原文链接:https://qwert.blog.csdn.net/article/details/105873239

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值