数据库多表关系


数据库多表关系


今天整理的什么勾八东西 一团浆糊 学废了

为什么需要多表数据存储

  • 如果所有关联数据都存在一个表中,数据会产生大量冗余
  • 拆表存储以后,使用外键 值替代引用数据,防止数据冗余
  • 拆表以后,产生多表查询,关联的数据表一定存在主外键关系!

表关系理解

  • 关系型数据库数据是按照类别进行存储,类别 - >表
  • 表之间存在关系【主外键
  • 表关系具体:表关系并不是表容器的关系,表关系的具体体现是数据的关系!
  • 表关系种类
    • 一对一
    • 一对多
    • 多对一
    • 多对多
  • 表关系:要双方对比,最后总结结果

表关系种类【主外键关系】

  • 一对一
  • 一对多
  • 多对多

表关系实现(表设计者的角度)

一对一实现

CREATE DATABASE day04;
USE day04;

# 学生基本信息表
CREATE TABLE base_info(
   bid INT PRIMARY KEY AUTO_INCREMENT COMMENT '学生基本信息主键',
   NAME VARCHAR(20) NOT NULL COMMENT '学生名'
)
# 方案1: 外键添加唯一约束即可 1:1
CREATE TABLE detail_info(
   did INT PRIMARY KEY AUTO_INCREMENT COMMENT '详情表自己的主键',
   address VARCHAR(200) NOT NULL COMMENT '详情地址',
   bid INT UNIQUE COMMENT '学生的基本信息外键',
   CONSTRAINT b_d_fk FOREIGN KEY (bid) REFERENCES base_info(bid)
)

#方案2: 外键+主键
CREATE TABLE detail_info1(
   address VARCHAR(200) NOT NULL COMMENT '详情地址',
   bid INT  PRIMARY KEY COMMENT '学生的基本信息外键,也是主键',
   CONSTRAINT b_de_fk FOREIGN KEY (bid) REFERENCES base_info(bid)
)

一对多实现

CREATE TABLE student(
  sid INT PRIMARY KEY AUTO_INCREMENT COMMENT "学生主键",
  sname VARCHAR (20) NOT NULL
)


CREATE TABLE score(
  cid INT PRIMARY KEY AUTO_INCREMENT  COMMENT "分数主键",
  score INT DEFAULT 0 COMMENT '分数',
  sid INT COMMENT '学生表外键',
  CONSTRAINT s_s_fk FOREIGN KEY (sid) REFERENCES student (sid)
)

多对多实现


SQL DQL语句(多表查询)

多表查询介绍和理解

  • 多表查询:就是将多个表的结果集合并到一起返回就是多表查询!
  • 多表查询方式:垂直结果拼接到一起,水平结果拼接查询
  • 垂直拼接:将两个结果集垂直方向拼接到一起,行和行数据之间没有任何的关联
  • 垂直拼接:两个结果集的查询的列数要一致,对应的类型一致即可!!不要求两个表有主外键关系
  • 水平拼接:将两个结果集水平拼接到一起,行和行数据之间要联动连接
  • 水平拼接:将两个结果集的不同行,拼接成一个新的行,要求两个表必须存在关系 【有主外键

垂直多表查询(合并结果集)

语法

#结果集1 UNION 结果集2;  UNION会去掉重复数据
#结果集1 UNION ALL 结果集2;  UNION ALL 不会去掉重复数据

注意

  • 要合并的结果集列数必须一样
  • 对应的列类型必须一致

表1

表1列名1 表1列名2
INT类型 VARCHAR类型

表2

表2列名1 表2列名2
INT类型 VARCHAR类型

代码实现

# 垂直合并结果集

# 数据准备
-- 创建表a 表b
CREATE TABLE a(
   aid INT,
   aname VARCHAR(10)
);

CREATE TABLE b(
   bid INT,
   bname VARCHAR(10)
);

-- 表中插入数据
INSERT INTO a VALUES(1,'aaaa'),(2,'bbbb'),(3,'cccc');
INSERT INTO b VALUES(4,'aaaa'),(2,'bbbb'),(3,'cccc'); 
#语法1
SELECT * FROM a
  UNION # 会去掉重复数据! 所有列都重复,就是重复数据!
SELECT * FROM b
  UNION
SELECT * FROM b;  
#语法2
SELECT * FROM a
  UNION ALL # 不会去掉重复数据! 所有列都重复,就是重复数据!
SELECT * FROM b
  UNION ALL
SELECT * FROM b;  

水平多表查询(水平连接查询)【重点】

水平查询明确 【连接查询为重点,99大法为辅助理解,自然连接是鸡肋,自连接是特殊方式】

  • 将多个结果集水平拼接,行和行要融合
  • 两个表之间必须存在关系 【主外键】

99查询大法【方言】

实现思路

  • 就是模仿99乘法表
  • 将两个表的数据全部水平拼接一遍,一定包含所有正常的组合
  • 也会包含错误的组合
  • 还需要自己使用WHERE进行条件筛选【有主外键】
  • 其实就是内连接的另外写法

实现语法

  • select * from 表1,表2,表3...表n where 主键 = 外键 and 主键 = 外键...
  • 如果有n张表,就有n-1对主外键条件!

实现代码

准备数据
# 准备数据
 -- 学生表
CREATE TABLE student(
	stuid VARCHAR(10)PRIMARY KEY,
	stuname VARCHAR(50)
);
-- 添加数据
SELECT * FROM student;
INSERT INTO student VALUES('1001','张三峰');
INSERT INTO student VALUES('1002','张无忌');
INSERT INTO student VALUES('1003','王尼玛');
INSERT INTO student VALUES('1004','王老五');


-- 分数表  多表查询的数据
CREATE TABLE score(
	stuid VARCHAR(10),
	score INT,
	courseid INT,
	CONSTRAINT fk_stu_sco  FOREIGN KEY(stuid) REFERENCES student(stuid)
);
-- 添加数据
SELECT * FROM score;
INSERT INTO score VALUES('1001',98,1);
INSERT INTO score VALUES('1002',95,1);
INSERT INTO score VALUES('1002',67,2);
INSERT INTO score VALUES('1003',83,2);
INSERT INTO score VALUES('1003',57,3);


# 课程表
CREATE TABLE course(
   cid INT PRIMARY KEY,
   cname VARCHAR(20) COMMENT '课程名称',
   tid  INT COMMENT '授课老师编号'
);
-- 添加数据
INSERT INTO course(cid,cname,tid) 
VALUES (1,'java',1),(2,'php',2),(3,'h5',3);

# 教师表
CREATE TABLE teacher(
   tid INT PRIMARY KEY COMMENT '讲师编号',
   tname VARCHAR(20) COMMENT '讲师名称'
);
-- 添加数据
INSERT INTO teacher(tid,tname) 
VALUES (1,'迪迦'),(2,'戴拿'),(3,'盖亚');


查询
# 查询学生姓名和分数以及对应的学科 
# 全部数据查询
SELECT * FROM student AS s,score c ,course u;
 
# 数据赛选
	 
SELECT s.stuname,c.score,u.cname 
FROM student AS s,score c ,course u
WHERE s.stuid = c.stuid 
AND c.courseid = u.cid ;
        
 # 查询学生姓名和分数以及课程已经对应的讲师姓名
 # 四张表! 水平拼接
 # 1.全部查询
 SELECT * FROM student s,score c,course u,teacher t;
 # 2.数据赛选
 SELECT s.stuname,c.score,u.cname,t.tname 
 FROM student s,score c,course u,teacher t
 WHERE s.stuid = c.stuid 
 AND c.courseid = u.cid 
 AND u.tid = t.tid
 

连接查询【普通话】

内连接

语法
#a. select  列  from 表1 [ inner] join 表2  on 主= 外  [inner] join 表3  on  主 = 外  where  group by ...
#b. 规律:连接查询,就是两张表连接一次,on一次主外键,再连接第三张表,以此类推即可!!!

代码
# 2.1 内连接 
# select * from 表1 别名 [inner] join 表2 别名 on 主 = 外 where group by  having order by limit 

# 使用内连接查询下你学生和学生分数
# 将两个表连接到一起,也有笛卡尔积  20条数据!
# from s,c
SELECT * FROM student s  INNER JOIN score c

# on 只写主外键!
SELECT * FROM student s  INNER JOIN score c ON s.stuid = c.stuid

# 内连接!!!! inner可以省略
SELECT * FROM student s JOIN score c ON s.stuid = c.stuid

# 查询学生姓名和分数以及课程名称!

# 学生表 分数表 课程表 【水平查询,99 还是 内外连接都要求有主外键!】
# 两两连接,
SELECT * FROM student s INNER JOIN score c ON s.stuid = c.stuid
		    INNER JOIN course u  ON c.courseid = u.cid;	
		    
#学生姓名和分数以及课程以及授课老师!
SELECT s.stuname,c.score,u.cname,t.tname FROM student s INNER JOIN score c ON s.stuid = c.stuid	
		    INNER JOIN course u ON c.courseid = u.cid
		    INNER JOIN teacher t ON u.tid = t.tid
		    WHERE c.score > 60;		
		    
# 没有三张以上的表查询! 我们都是两两处理!主外键!
# 最后如果有where 再添加即可!

外连接

语法
#语法:
SELECT * FROM 表1 [inner] JOIN  表2 ON 主 = 外
#外连接:
select * from 表1 left/right [outer] join  表2 on 主 = 外
#left和right的作用就是用来指定逻辑主表!
代码
#99大法和内连接查询效果是一样! 
#他们都是一种公平查询法则,要求两个表必须存在主外键的值,结果才能显示!例如:没有王老五, 因为学生表王老五,但是分数表没有外键的值! 结果没有王老五!

#外连接: 就是了结果显示王老五!外连接不是公平查询法则,他可以通过左和右方向来指定逻辑主表!
#一旦被指定逻辑主表
#逻辑主表的数据再结果中全部会显示! 如果想要王老五,必须使用外连接!!

# 查询学生和分数

SELECT * FROM score c  RIGHT OUTER JOIN student s ON c.stuid = s.stuid;

#方向就是指定你想要全部数据的表!
-- 两张表
SELECT * FROM student s  LEFT  JOIN  score c ON c.stuid = s.stuid;

-- 如果是三张表呢?
SELECT * FROM student s
LEFT  JOIN  score c ON c.stuid = s.stuid
LEFT JOIN course u ON c.courseid = u.cid
LEFT JOIN teacher t ON u.tid = t.tid;  

# left / right 外连接     join 内连接

内外连接的选择

  • 只要有一个表的数据必须要全部展示,外连接!
  • 规律
    • 一旦你选择了外连接,那么后面的所有连接都是外连接!并且连接方向都一致!!

内外连接的区别

  • 外连接有王老五,内连接没有王老五!

  • 内连接和99大法他们是一类语法,都是公平法则,最后结果的数据要求双方都存在引用!查询没有王老五!

  • 外连接是非公平查询法则,可以通过方向指定左或者右为逻辑主表,主表的数据一定再结果中存在! 查询有王老五!

自然连接

  • 自连接也是多表查询,不是新的语法,只是新的方式!
  • 如果需要的数据来自于一行,那么就是单表查询!
  • 如果需要的数据来自于多行,那么就是多表查询!
  • 表查询分为两种,一种正常不同多表【学生和分数】 另一种 自连接 【查员工和经理】
  • 自连接:因为在设计表的时候,引用数据和被引用数据都存在一个表!还是不同行!

代码练习

# 4. 自连接 - 自己连接自己  
/*
  他不是一种新的语法,他是一种新的查询方式!
   一个表使用多次!
   学生表 连接 学生表  -》 自连接
*/       

编写一个 SQL 查询,该查询可以获取收入超过他们经理的员工的姓名
CREATE TABLE IF NOT EXISTS Employee (Id INT, NAME VARCHAR(255), 
Salary INT, ManagerId INT)

INSERT INTO Employee (Id, NAME, Salary, ManagerId) VALUES ('1', 'Joe', '70000', '3');
INSERT INTO Employee (Id, NAME, Salary, ManagerId) VALUES ('2', 'Henry', '80000', '4');
INSERT INTO Employee (Id, NAME, Salary, ManagerId) VALUES ('3', 'Sam', '60000', 'None');
INSERT INTO Employee (Id, NAME, Salary, ManagerId) VALUES ('4', 'Max', '90000', 'None');

Employee 表包含所有员工,他们的经理也属于员工。
每个员工都有一个 Id,此外还有一列对应员工的经理的 Id。

+----+-------+--------+-----------+
| Id | NAME  | Salary | ManagerId |
+----+-------+--------+-----------+
| 1  | Joe   | 70000  | 3         |
| 2  | Henry | 80000  | 4         |
| 3  | Sam   | 60000  | NULL      |
| 4  | MAX   | 90000  | NULL      |
+----+-------+--------+-----------+

给定 Employee 表,编写一个 SQL 查询,该查询可以获取收入超过
他们经理的员工的姓名。
在上面的表格中,Joe 是唯一一个收入超过他的经理的员工。

+----------+
| Employee |
+----------+
| Joe      |
+----------+

# 1. 查询员工姓名和经理名
   joe     sam 
   henry   MAX 
   
# 2. 行内数据对比   
   
SELECT  e1.*,e2.* 
FROM Employee e1 
INNER JOIN  Employee e2 ON e1.ManagerId = e2.Id
WHERE e1.Salary > e2.Salary;

子查询/嵌套查询

概念

  • 子查询也叫嵌套查询
  • 子查询就是查询中嵌套其他的查询
  • 子查询产生的原因:没有得到直接条件,得到了间接条件,需要根据间接条件返回数据再次查询!

语法

#查询中嵌套查询
#比如 和小明身高一样的同学
 SELECT * FROM student 
 WHERE height = (select height from student where name = '小明');

子查询位置

单行单列

一般放在WHERE后面,充当一个条件值,可以使用任何比较符号 > < = ! =

代码
#1.工资高于JONES的员工 【单行单列】
# 1.查询JONES的工资 单行单列
SELECT sal FROM emp WHERE ename ='JONES'
# 2.查询比他工资高的员工信息  > < =
SELECT * FROM emp WHERE sal > (SELECT sal FROM emp WHERE ename ='JONES')
#2.SCOTT同一个部门的员工 【单行单列】
#1.查询SCOTT的部门
SELECT deptno FROM emp WHERE ename = 'SCOTT'
#2.查询这个部门的员工
SELECT * FROM emp WHERE deptno = (SELECT deptno FROM emp WHERE ename = 'SCOTT')

多行单列

一般放在where后面,充当多个条件值

多列等于比较: 例如:查询性别和身高与小明一样的同学! 可以使用 in关键字 (列名,列名) in (子查询 列名 ,列名) , 注意一一对应

多列等于比较:例如:查询性别一样但是身高高于小明的同学! 不用in了,拆分成单行单列再比较即可!

代码
#4.查询工作和工资与MARTIN完全相同的员工信息
# 1。查询martin的工作和工资
SELECT job,sal FROM emp WHERE ename = "MARTIN"
# 2. 多列等于
SELECT * FROM emp 
WHERE (job,sal) 
IN (SELECT job,sal FROM emp WHERE ename = "MARTIN")
  
SELECT * FROM emp 
WHERE job = (SELECT job FROM emp WHERE ename = 'MARTIN')
AND sal = (SELECT sal FROM emp WHERE ename = 'MARTIN')

多行多列

都放在from 充当虚拟表!子查询结果集做表,****必须要起别名 from (子查询) 别名

代码
#5.有2个以上直接下属的员工信息
# 1.分组查询 有几个下属
SELECT 分组字段,聚合函数
# 查询出有两个以上下属的经理id和下属数量
SELECT mgr,COUNT(1) ct FROM emp  GROUP BY mgr HAVING ct >2;
    
#2.虚拟结果充当表在和员工表连接一次
SELECT e1.empno,e1.ename,temp.ct FROM emp e1 JOIN 
(SELECT mgr,COUNT(1) ct FROM emp  GROUP BY mgr HAVING ct >2) temp
ON e1.empno = temp.mgr;

子查询总结

  • 子查询就是间接条件查询
  • 子查询可以出现在单表,也可以出现在多表

相关