MySQL多表查询(通俗易懂)--持续更新

本文介绍了数据库设计中的范式理论,通过实例展示了第一范式的重要性,解释了一对多关系的表结构调整,探讨了级联删除的功能,并提供了多表查询的几种方式,包括内连接、外连接和子查询的应用。内容涵盖了数据库规范化、数据冗余减少和关系型数据库管理。
摘要由CSDN通过智能技术生成

前置概念

注意:在下面sql语句中,关键词没有用大写,主要为了通俗易懂,大家在写的时候应该还是要大写的。


1.数据库表不规范设计的缺陷引出

首先我们创建一个数据库test_db

然后创建一个员工表:employee

  • 字段有
    • e_name 员工名称
    • e_age 员工年龄
    • dep_name部门名称
    • dep_location部门位置
# 1.创建test_db数据库
create database test_db;

# 2.使用test_db数据库
use test_db;

# 3.创建员工表
create table employee(
	e_name varchar(20) comment '员工名称',
	e_age int comment '员工年龄',
	dep_name varchar(20) comment '部门名称',
	dep_location varchar(20) comment '部门位置'
);

# 4.员工表添加数据
insert into employee values('CodingGuan',13,'研发部','广州');
insert into employee values('刘诗诗',14,'运营部','广州');
insert into employee values('范冰冰',15,'销售部','深圳');
insert into employee values('刘德华',16,'研发部','广州');
insert into employee values('张学友',17,'销售部','深圳');
insert into employee values('张三',18,'运营部','广州');

查看员工表数据

select * from employee;

问题

  • 一个表中出现了两类数据:部门数据和员工数据
  • 出现重复数据:没创建一个角色的时候都需要写重复的部门名称以及部门位置

解决方案

  • 通过数据库设计范式的第一范式
    • 数据库设计范式作用:建立冗余较小结构合理的数据库,设计数据库时候遵循的规矩
  • 在这里我们遵循数据库第一范式
    • 第一范式的概念:原子性,做到列不可以拆分位置,每个字段都是不可再分的最小数据单元

在满足第一范式我们还需要满足第二范式及第三范式,这个知识大家可以后面通过博客内容进行了解,这里不做过多描述。


所以我们将上面的表拆分为:员工表、部门表

  • 从中还引出一个特点
  • 1个部门有很多员工,员工属于一个部门,一对多的关系
  • 员工是跟随部门的存在所存在,所以:部门-主表;员工:从表

2.不规范改进

部门表

  • 部门编号:id
  • 部门名称:dep_name
  • 部门位置:dep_location

员工表

  • 员工编号:id
  • 员工名称:emp_name
  • 员工年龄:emp_age
  • 部门编号:dep_id(注意:外键约束中,约束的数据类型应该一致)

sql语句

# 1.删除刚刚创建的employee表
drop table employee;

# 2.创建部门表(部门的位置还可以在分)
create table department(
	id int primary key auto_increment comment '部门编号',
	dep_name varchar(20) comment '部门名称',
	dep_location varchar(20) comment '部门位置'
);

知识补充:创建、添加、修改外键约束的语法

  • 创建表的时候添加外键约束

constraint[可以省略] 外键约束名称(可以省略) foreign key (本表中要建立约束的外键字段) references 主表表名(主表字段)

  • 创建表完成后在添加外键

alter table 从表 add constraint[可以省略] 外键约束名称(可以省略) foreign key (本表中要建立约束的外键字段) references 主表表名(主表字段)

  • 删除根据外键名称外键约束

alter table 从表 drop foreign key 外键约束的名称.

# 3.创建员工表
create table employee(
	id int primary key auto_increment comment '员工编号',
	emp_name varchar(20) comment '员工名称',
	emp_age int comment '员工年龄',
	dep_id int comment '外键约束:部门编号',
	CONSTRAINT dep_id_for foreign key(dep_id) references department(id)
);

注意:

  • 由于建立了约束,所以我们在添加数据的时候,应该先添加部门表的数据,然后再去添加员工表的数据。
  • 因为两个表建立了约束关系,主表拥有的数据从表才能拥有,从表不能添加主表以外的数据
# 4.添加部门表数据
insert into department values(1,'研发部','广州');
insert into department values(2,'运营部','广州');
insert into department values(3,'销售部','深圳');

# 5.员工表添加数据
insert into employee values(1,'CodingGuan','13',1);
insert into employee values(2,'刘诗诗','14',2);
insert into employee values(3,'范冰冰','15',3);
insert into employee values(4,'刘德华','16',1);
insert into employee values(5,'张学友','17',3);
insert into employee values(6,'张三','18',2);

 员工表

select * from employee;

部门表

select * from department;

 这样我们的数据就可以比较的清晰,但是存在一些缺陷,例如dep_location部门位置还可以在衍生出一个表,这些交给大家去实现了,还有数据结构不规范等。


3.级联删除

  • 我们在没有使用级练删除之前,我们删除上面两个表的数据,因遵循的规则是:先删除员工表的数据,当部门下没有员工数据的时候,在删除部门数据
  • 如果直接删除部门数据,就会报错

例如我们直接删除部门表中研发部的数据:

就会报外键约束的错误:a foreign key constraint fails

# 删除研发部数据
delete from department where dep_name = '研发部';

 所以我们应该删除员工数据,在删除部门数据

例如下面操作:

# 先删除员工数据
delete from employee where dep_id = 1;

# 然后删除研发部数据
delete from department where dep_name = '研发部';

如果我们感觉这种删除的方式有点费劲,想删除部门数据的数据的时候,就删除员工的数据,这时级联删除就出来了

级联删除概念:

  • 删除主表数据的同时,也删除掉从表数据

  • 在添加表约束语句后面添加on delete cascade

  • cascade(n.)级联

例如

我们先删除刚刚创建的外键约束

# 删除员工表和部门表的约束
alter table employee drop foreign key dep_id_for;

然后我们添加外键约束同时标识为级联删除

alter table employee add constraint dep_id_for foreign key (dep_id) references department(id) on delete cascade;

 

在刚刚表数据基础上我们直接删除运营部数据

delete from department where dep_name = '运营部';

可以看到,我们轻而易举的就删除了数据,这就是级联删除的用法。


4.多表查询

在了解多表查询的时候,我们先了解表和表之间有什么关系。

在表和表的关系中存在着三种关系

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

下面我们对着三种关系进行了解


一对一

  • 在一对一关系表中,例如我们一个人对应一个身份证。
  • 建表原则
  • 主表的主键和从表的外键(唯一),使用unique唯一关键字
# 创建身份证表
create table person_id_card(
	card_number varchar(20) UNIQUE comment '身份证号码'
);

# 创建一个人表
create table person(
	id int primary key auto_increment comment '人编号', 
	name varchar(20) comment '人名字',
	card_number varchar(20) UNIQUE,
	CONSTRAINT card_number_for FOREIGN key (card_number) REFERENCES person_id_card(card_number)
);

# 插入身份证
insert into person_id_card values('44192939999929299');

# 插入员工
INSERT into  person values(1,'张三','44192939999929299');

一对多

  • 例如我们一个部门有多个员工。一个班级有多名学生。
  • 建表规则:在从表(多的一方)设立一个字段,指向主表的字段


多对多关系

例如

  • 我们老师教很多学生,学生有很多老师。
  • 一个学生可以上很多课,一门课,也有很多学生学习。
  • 建表规则:创建第三张表,第三张表设立两个字段,分别指向各表的主键。


多表查询相关操作

数据准备

创建分类表和商品表


# 多表查询
# 创建分类表. 一个分类有很多商品。分类:主表.  商品:从表
create table category(
	c_id varchar(30) primary key  comment '分类编号',
	c_name varchar(30) comment '分类名称'
);
show tables;
# 创建商品表
create table product(
	p_id varchar(32) primary key  comment '商品编号',
	p_name varchar(30) comment '商品名称',
	p_price int comment '商品价格',
	flag varchar(2) comment '是否上架标记:1>上架;0>下架',
	category_id varchar(30) comment '主表约束字段',
	constraint c_id_for foreign key (category_id) REFERENCES category(c_id)
);

# 分类表添加数据
insert into category values ('c001','家电');
insert into category values ('c002','鞋服');
insert into category values ('c003','化妆品');
insert into category values ('c004','汽车');


# 商品表添加数据

INSERT INTO product VALUES('p001','小米电视机',5000,'1','c001');
INSERT INTO product VALUES('p002','格力空调',5000,'1','c001');
INSERT INTO product VALUES('p003','美的冰箱',5000,'1','c001');
INSERT INTO product VALUES('p004','篮球鞋',800,'1','c002');
INSERT INTO product VALUES('p005','运动裤',200,'1','c002');
INSERT INTO product  VALUES('p006','T恤',300,'1','c002');
INSERT INTO product  VALUES('p007','冲锋衣',2000,'1','c002');
INSERT INTO product  VALUES('p008','神仙水',800,'1','c003');
INSERT INTO product VALUES('p009','大宝',200,'1','c003');

默认情况下我们直接进行两个表查询,就会出现笛卡尔积现象

  • 笛卡尔积现象简单的说就是:A表XB表 = 数据集合

例如:我们查询商品表和分类表

# 笛卡尔积:两张表数据直接查询,获取AxB表的数据(冗余)
select * from category,product;

 我们肯定不要这种数据,所以我们就出现了了多表查询一些操作,例如内连接、外连接等等。


内连接

  • 在内连接中,就是通过指定的条件匹配两张表的数据,匹配上就显示匹配不上就显示
  • 内连接一般分为两种
    • 隐式内链接:用SQL92写法
      • 特点:用where指定连接的条件
    • 显示内连接:用SQL99写法
      • 特点:用表1 [inner] join 表2 on的方式指定连接的条件

我们首先看隐式内链接:

例如:

  • 查询所有商品名称p_name、商品价格p_price,并且显示商品分类的名称c_name
  • 商品表的分类编号p_category_id和分类表的分类编号c_id对应,根据这个进行多表连接
select p.p_name,p.p_price,c.c_name
from category c,product p 
where c.c_id = p.category_id;


现在我们一般使用显示内连接:

语法格式:

select 字段名

from 左表  [inner] join 右表

on 条件;

注意:inner可以省略

还是跟上面一样:

例如:

  • 查询所有商品名称p_name、商品价格p_price,并且显示商品分类的名称c_name
  • 商品表的分类编号p_category_id和分类表的分类编号c_id对应,根据这个进行多表连接
# 显示内链接 表1 [inner] join 右表 on 条件
select p.p_name,p.p_price,c.c_name
from category c inner join product p
on c_id = p.category_id;


 左外连接

  • 左外连接就是,左边不管匹配不匹配都全部显示,右边是匹配的就显示,匹配不上就不显示
  • 语法: 表1 left [inner] join 表2 on 条件

例如:

  • 我们查询在分类下,每个商品都个数有多少
    • 分类是左表,显示所有分类
    • 商品是右表,因为商品有分类才记录,没有分类就不记录
# 左外链接 左边全部数据,右边部分数据
# 查询每个分类下商品的个数 count(p.p_name) 会输出对应记录数
select c.c_name,count(p.p_name)
from product p right join category c
on p.category_id = c.c_id
group by c.c_name;

右外连接

  • 右外连接就是,右边不管匹配不匹配都全部显示,左边匹配到右表就显示,没有匹配就不显示。
  • 语法:表1 right [inner] join 表2 on 条件
  • 我们可以把上面的换个思想应用就可以

子查询

  • 自查询简单来说就是将一个select查询出来的结果给另一个select语句使用
  • 例如:select * from 表 where 字段 > (select ... from ...)这样的,用一个小括号包着的

通过案例来了解下:

例如:子查询查询出最高的商品信息。

  • 查询商品最贵的(MAX(p_price)) 的商品编号给另一条select语句使用
select * from product
where p_price = (select (MAX(p_price)) from product);

例如:查询化妆品分类下的商品名称和商品价格

select p_name,p_price,category_id
from product 
where category_id = (select c_id from category where c_name='化妆品');

例如: 查询商品价格小于平均价格的商品信息

select * from product 
where p_price < (select AVG(p_price) from product);

例如:查询商品中,价格大于500的商品信息,商品名称,商品价格,商品所属分类名称

select p.p_name,p.p_price,c.c_name
from product p join category c
on p.category_id = c.c_id
where p_id in (select p_id from product where p_price>500);

select p.p_name,p.p_price,c.c_name
from product p JOIN (select * from category) c
on p.category_id = c.c_id
where p.p_price>500;

基本内容就到这里,后续将继续推送多表查询的相关例子。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bboy_guan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值