当前位置: 首页 > news >正文

全面解析MySQL(4)——三大范式与联合查询实例教程

1.三大范式

概念:数据库三大范式(Normal Forms)是关系型数据库设计中的基本原则,用于减少数据冗余和提高数据一致性

1.1 第一范式(1NF)

定义:确保每一列的原子性,即每一列都是不可再分的最小数据单元 要求:

  • 每个字段只能包含单一值,不能是集合数组
  • 表中不能有重复的列。关系模型的创始人 E.F. Codd 最初提出范式理论时,要求关系表必须有一个主键/联合主键(唯一标识每一行的字段或字段组合)

示例:订单表

订单ID(order_id)客户信息(customer_info)
1张三,四川省XXX市,17392871691
  • 无法单独查询四川省的客户信息(insert into table_name where address = '四川省')
  • 更新客户地址时需要解析字符串(可能需要借助后端额外处理)

修正后示例:

订单ID(order_id)客户姓名(user_name)地址(address)电话号码(tele)
1张三四川省XXX市17392871691

1.2 第二范式(2NF)

定义:在满足第一范式的基础上,消除非主键字段对主键的部分函数依赖(即非主键字段必须完全依赖整个主键/联合主键,而非部分主键)
联合主键(Composite Primary Key):是指由数据库表中两个或多个字段共同组成的主键,用于唯一标识表中的每一行数据。与单字段主键不同,联合主键通过多个字段的组合确保数据的唯一性

示例:学生-课程成绩表
使用学号+课程名唯一标识某一行记录,这两列就是该表的联合主键

学号(student_id)姓名(student_name)性别(sex)课程名(course_name)学分(credit)分数(score)
1张三MySQL480
2李四Java475
1张三Java470
3王五MySQL485

姓名、性别两列由学号确定,和课程名没关系,即姓名学号两列部分依赖学号学分课程名确定,和学号没关系,即学分这列部分依赖课程名

  • 数据冗余:姓名、性别、学分三列在表中重复出现,造成大量冗余
  • 更新异常:更新Java这门课的学分时,如果因为某些意外而中断更新,就会导致该表中Java的学分不一致

修正后示例:
学生表

学号(id)姓名(name)性别(sex)
1张三
2李四
1张三
3王五

课程表

课程ID(id)课程名(course_name)学分(credit)
1MySQL4
2Java4

分数表

学号课程ID(id)分数(score)
1180
1270
2275
3185

1.3 第三范式(3NF)

定义:在满足第二范式的基础上,消除非主键字段之间的传递依赖(即非主键字段不能依赖于其他非主键字段)

示例:员工部门表

员工ID姓名部门ID部门名称
1张三研发部北京总部
2李四市场部上海分部
3王五研发部北京总部

部门名称依赖于部门ID,而不是直接依赖于员工ID,存在传递依赖:员工ID → 部门ID → 部门信息

  • 数据冗余:研发部名称重复存储
  • 更新异常:修改部门所在地需要更新多条记录

2.联合查询

2.1 概述

使用场景:在数据设计时由于三大范式的要求,数据被拆分到多个表中,那么要查询⼀条数据的完整信息,就要从多个表中获取数据,这时就要使用联合查询

笛卡尔积(Cartesian Product):联合查询的基础,指两个集合中所有可能的有序组合。在MySQL中,当联合查询未指定关联条件时,查询结果会返回所有表的行组合(列相加,行相乘)

学生表

学号姓名性别班级编号
1张三1
2李四3
3王五2

班级表

编号班级
1Java班
2C++班
3Python班

两表进行笛卡尔积

学号姓名性别班级编号编号班级
1张三11Java班
1张三12C++班
1张三13Python班
2李四31Java班
2李四32C++班
2李四33Python班
3王五21Java班
3王五22C++班
3王五23Python班

笛卡尔积生成的结果集可能包含无效或冗余数据。无效数据通常指不符合业务逻辑、无实际意义或违反约束条件的组合。上述笛卡尔积的结果集中,只有学生表的班级编号与班级表的编号相同的记录才有实际意义

学号姓名性别班级编号编号班级
1张三11Java班
2李四33Python班
3王五22C++班

构造练习数据:

-- 创建学生表
create table classes (class_id int primary key auto_increment,class_name varchar(50) not null,teacher_name varchar(50),created_at timestamp default current_timestamp
);
-- 插入班级数据
insert into classes (class_name, teacher_name) values
('计算机科学1班', '张老师'),
('计算机科学2班', '李老师'),
('数学实验班', '王老师'),
('英语提高班', '赵老师'),
('物理竞赛班', NULL);  -- 这个班级暂时没有分配老师
--创建班级表
create table students (student_id int primary key auto_increment,student_name varchar(50) not null,gender char(1) check (gender in ('M', 'F')),age int,class_id int,enrollment_date date,foreign key (class_id) references classes(class_id) on delete set null 
);
-- 插入学生数据
insert into students (student_name, gender, age, class_id, enrollment_date) values
('张三', 'M', 20, 1, '2023-09-01'),
('李四', 'M', 21, 1, '2023-09-01'),
('王五', 'F', 19, 2, '2023-09-01'),
('赵六', 'F', 20, 2, '2023-09-02'),
('钱七', 'M', 22, 3, '2023-09-03'),
('孙八', 'F', 20, 3, '2023-09-03'),
('周九', 'M', 21, NULL, '2023-09-04'),  -- 这个学生尚未分班
('吴十', 'F', 19, NULL, '2023-09-05'); -- 这个学生尚未分班

2.2 内连接(inner join)

定义:在笛卡尔积的有效结果集基础上,仅返回满足条件的记录(如果不指定其他条件,内连接的结果集和笛卡尔积的有效结果集完全一样)
在这里插入图片描述

基础语法:

#语法1
select 字段 from1 别名1,2 别名2 where 连接条件 and 其他条件;
#语法2
select 字段 from1 别名1 [inner] join2 别名2 on 连接条件 where 其他条件;

示例:查询(student_id <= 3)的学生信息和班级信息
写法1: 在这里插入图片描述
写法2: 在这里插入图片描述

2.3 外连接

2.3.1 左外连接(left outer join)

定义:返回左表的所有/指定行,即使在右表中没有匹配的行(右表中未匹配的部分用null填充)。换言之,在有效笛卡尔积基础上增加未能与右表匹配的行,这些新增行的右表部分用null值填充在这里插入图片描述

基础语法:

select 字段 from 表名1 left outer join 表名2 on 连接条件;

示例:
在这里插入图片描述

2.3.2 右外连接(right outer join)

定义:返回右表的所有行,即使在左表中没有匹配的行(左表中未匹配的部分用null填充)。换言之,在有效笛卡尔积基础上增加未能与左表匹配的行,这些新增行的左表部分用null值填充在这里插入图片描述

基础语法:

select 字段 from 表名1 right outer join 表名2 on 连接条件;

示例:
在这里插入图片描述

2.3.3 全外连接(full outer join,MySQL不支持)

定义:返回左表和右表的所有行,未匹配的部分用null填充。若某行在另一表中有匹配,则显示匹配值在这里插入图片描述

2.4 自连接

使用场景:实现行与行之间的比较
在这里插入图片描述
定义:自连接(Self Join)是SQL中的一种特殊连接操作,指同一张表通过别名(as)模拟成两张表,将行与行之间的关系转换为列与列之间的关系,并根据关联条件进行连接查询在这里插入图片描述

基础语法:

select 字段 from table_name as 别名1,table_name as 别名2 where 连接条件 and 其他条件;

示例:查询数学成绩高于语文成绩的记录
在这里插入图片描述

2.5 子查询(Subquery)

定义:指一个SQL语句查询内部嵌套另一个完整的select查询语句

构造数据

#学生表
create table students (student_id int primary key,name varchar(50) NOT NULL,class_id int,gender char(1),age int
);
insert into students values
(101, '张三', 1, '男', 18),
(102, '李四', 1, '女', 17),
(103, '王五', 2, '男', 19),
(104, '赵六', 2, '女', 18),
(105, '钱七', 3, '男', 17);
#成绩表
create table scores (score_id int primary key,student_id int,subject varchar(20),score int,foreign key (student_id) references students(student_id)
);
insert into scores values
(1, 101, '数学', 85),
(2, 101, '英语', 78),
(3, 102, '数学', 92),
(4, 102, '英语', 88),
(5, 103, '数学', 65),
(6, 103, '英语', 72),
(7, 104, '数学', 55),
(8, 104, '英语', 91),
(9, 105, '数学', 82),
(10, 105, '英语', 77);

2.5.1 单行子查询

定义:单行子查询指返回单个值(一行一列)的子查询,主查询将子查询结果作为标量值使用

基础语法:

select 列名 from table1 where 列名 = (select 列名 from table2...);

示例:查询张三的数学成绩

#先在students表中查询到张三的student_id
select student_id from students where name = '张三';
#再在scores表中利用查到的student_id来查询张三的数学成绩
select subject,score from scores where subject = '数学' and student_id = 101;#合并后的子查询
select subject,score from scores where subject = '数学' and student_id = 
(select student_id from students where name = '张三');

在这里插入图片描述

2.5.2 多行子查询

定义:主查询将子查询返回的多行单列结果集作为条件使用

基础语法:

select 列名 from table1 where 列名 in (select 列名 from table2...);

示例:查询数学成绩大于80的学生姓名

#先在scores表中查询数学成绩大于80的student_id
select student_id from scores where subject = '数学' and score > 80;
#再在students表中利用查到的student_id来姓名
select name from students where student_id in (101,102,105);#合并后的子查询
select name from students where student_id in 
(select student_id from scores where subject = '数学' and score > 80);

在这里插入图片描述

2.5.3 from子句中的子查询

定义:子查询作为临时表嵌入from子句(称为派生表),需指定别名

示例:查询成绩大于平均成绩的记录

select * from scores as s,(select avg(score) as avg_score from scores) as a 
where s.score > a.avg_score;

在这里插入图片描述

2.6 合并查询

定义:合并查询(Set Operation Queries)是SQL中用于组合多个select结果集的操作
要求:需要查询的列数和数据类型匹配,列名不影响

基础语法1:

select col_1,col_2 ... from table_1
union
sekect col_1,col_2 ... from table_2;
  • 自动去除重复行

在这里插入图片描述
基础语法2:

select col_1,col_2 ... from table_1
union all 
sekect col_1,col_2 ... from table_2;
  • 保留重复行
  • 性能优于union

在这里插入图片描述

http://www.dtcms.com/a/301933.html

相关文章:

  • 【Java Web实战】从零到一打造企业级网上购书网站系统 | 完整开发实录(终)
  • Linux DNS解析2 -- 网关DNS代理的作用
  • CodeMeter授权管理方案助力 PlantStream 引领工业设计新变革
  • 接口测试怎么做?接口测试工具有哪些?
  • JavaWeb 入门:HTML 基础与实战详解(Java 开发者视角)
  • 使用JavaScript实现一个代办事项的小案例
  • 基于亮数据 MCP 的 Trae 智能体,让规模化 Google 数据实时分析触手可及
  • MCP资源管理深度实践:动态数据源集成方案
  • 剑指“CPU飙高”问题
  • 从视觉到智能:RTSP|RTMP推拉流模块如何助力“边缘AI系统”的闭环协同?
  • Entity Framework Core (EF Core) 中状态检测
  • 编程算法:技术创新的引擎与业务增长的核心驱动力
  • 【前端】Tab切换时的数据重置与加载策略技术文档
  • HTB赛季8靶场 - era
  • 可以组成网络的服务器 - 华为OD统一考试(JavaScript 题解)
  • S7-200 SMART 通过本体 RS485 口与 DP01 上传 / 下载程序(网口故障)
  • FastGPT本地构建工作流高级编排(最新4.11.0)
  • Windows 11 下 Anaconda 命令修复指南及常见问题解决
  • Linux应用开发基础知识——LInux学习FreeType编程(七)
  • 【Linux | 网络】传输层(UDP和TCP) - 两万字详细讲解!!
  • 绿算技术携手昇腾发布高性能全闪硬盘缓存设备,推动AI大模型降本增效
  • LeetCode--50.Pow(x,n)
  • MySQL的常用数据类型详解
  • python毕业设计案例:基于python django的抖音数据分析与可视化系统,可视化有echarts,算法包括lstm+朴素贝叶斯算法
  • Java项目:基于SSM框架实现的社区团购管理系统【ssm+B/S架构+源码+数据库+毕业论文+答辩PPT+远程部署】
  • PyTorch入门动态图与神经网络构建
  • PostgreSQL 14.4 ARM64 架构源码编译安装指南
  • 【运维】HuggingFace缓存目录结构详解
  • MySQL SQL性能优化与慢查询分析实战指南:新手DBA成长之路
  • 【第四章:大模型(LLM)】01.神经网络中的 NLP-(2)Seq2Seq 原理及代码解析