文章目录

  • 数据库范式设计
    • 范式级别
    • 数据表中的键
    • 从1NF到3NF
    • BCNF
    • 总结
  • 复杂查询
    • 1. 链接查询
      • 1.1 交叉连接
      • 1.2 等值连接
        • 1.2.1 自然连接
        • 1.2.2 USING连接
        • 1.2.3 ON连接
      • 1.3 非等值连接
      • 1.4 外连接
        • 1.4.1左外连接
        • 1.4.2右外连接
        • 1.4.3全外连接
      • 1.5 自连接
    • 2.联合查询
    • 3.子查询
      • 3.1 EXISTS 子查询
      • 3.2 集合比较子查询
      • 3.3 子查询作为计算字段

数据库范式设计

范式是数据表设计的基本原则,又很容易被忽略。很多时候,当数据库运行了一段时间之后,我们才发现数据表设计得有问题。重新调整数据表的结构,就需要做数据迁移,还有可能影响程序的业务逻辑,以及网站正常的访问。所以在开始设置数据库的时候,我们就需要重视数据表的设计.

范式级别

我们在设计关系型数据库模型的时候,需要对关系表各个字段之间联系的合理化程度进行定义,这就有了不同等级的规范要求,这些规范要求被称为范式(NF)。你可以把范式理解为,一张关系表的设计结构需要满足的某种设计标准的级别

  • 1NF(第一范式)
  • 2NF(第二范式)
  • 3NF(第三范式)
  • BCNF(巴斯 - 科德范式)
  • 4NF(第四范式)
  • 5NF(第五范式,又叫做完美范式)

数据库的范式设计越高阶,冗余度就越低,同时高阶的范式一定符合低阶范式的要求,比如满足 2NF的一定满足 1NF,满足 3NF 的一定满足 2NF,依次类推
一般来说数据表的设计应尽量满足 3NF。但也不绝对,有时候为了提高某些查询性能,我们还需要破坏范式规则,也就是反范式设计

数据表中的键

范式的定义会使用到主键和候选键(因为主键和候选键可以唯一标识元组),数据库中的键(Key)由一个或者多个属性组成

  • 超键:能唯一标识元组的属性集叫做超键。
  • 候选键:如果超键不包括多余的属性,那么这个超键就是候选键。
  • 主键:用户可以从候选键中选择一个作为主键。
  • 外键:如果数据表 R1 中的某属性集不是 R1 的主键,而是另一个数据表 R2 的主键,那么这个属性集就是数据表 R1 的外键。
  • 主属性:包含在任一候选键中的属性称为主属性。
  • 非主属性:与主属性相对,指的是不包含在任何一个候选键中的属性

简单的举个栗子:NBA球员表和球队表。

球员表:球员编号、姓名、身份证号、年龄和球队编号;
球队表:球队编号、主教练和球队所在地。
对于球员表来说,超键就是包括球员编号或者身份证号的任意组合,比如(球员编号)(球员编号,姓名)(身份证号,年龄)等。候选键就是最小的超键,对于球员表来说,候选键就是(球员编号)或者(身份证号)。主键是我们自己选定,也就是从候选键中选择一个,比如(球员编号)。外键就是球员表中的球队编号。在球员表中,主属性是(球员编号)(身份证号),其他的属性(姓名)(年龄)(球队编号)都是非主属性

从1NF到3NF

  • 1NF 指的是数据库表中的任何属性都是原子性的,不可再分。不同的业务,对不可再分的要求也不一样
  • 2NF 指的数据表里的非主属性都要和这个数据表的候选键有完全依赖关系.所谓完全依赖不同于部分依赖,也就是不能仅依赖候选键的一部分属性,而必须依赖全部属性

举个栗子:比如我们设计一张球员比赛表。
球员比赛表:球员编号、姓名、年龄、比赛编号、比赛时间和比赛场地,得分。
这里候选键和主键都为(球员编号,比赛编号),我们可以通过候选键来决定如下的关系:(球员编号,比赛编号) → (姓名, 年龄, 比赛时间, 比赛场地,得分)
但是这个数据表不满足第二范式,因为数据表中的字段之间还存在着如下的对应关系:
(球员编号) → (姓名,年龄), (比赛编号) → (比赛时间, 比赛场地)也就是说候选键中的某个字段决定了非主属性。你也可以理解为,对于非主属性来说,并非完全依赖候选键

这样会产生怎样的问题呢?

  1. 数据冗余:如果一个球员可以参加 m 场比赛,那么球员的姓名和年龄就重复了 m-1 次。一个比赛
    也可能会有 n 个球员参加,比赛的时间和地点就重复了 n-1 次。
  2. 插入异常:如果我们想要添加一场新的比赛,但是这时还没有确定参加的球员都有谁,那么就没法插入。
  3. 删除异常:如果我要删除某个球员编号,如果没有单独保存比赛表的话,就会同时把比赛信息删除掉。
  4. 更新异常:如果我们调整了某个比赛的时间,那么数据表中所有这个比赛的时间都需要进行调整,否则就会出现一场比赛时间不同的情况

为了避免出现上述的情况,我们可以把球员比赛表设计为下面的三张表。

  • 球员表包含球员编号、姓名和年龄等属性;
  • 比赛表包含比赛编号、比赛时间和比赛场地等属性;
  • 球员比赛关系表包含球员编号、比赛编号和得分等属性。

这样的话,每张数据表都符合第二范式,也就避免了异常情况的发生。某种程度上 2NF 是对 1NF 原子性的升级。1NF 告诉我们字段属性需要是原子性的,而 2NF 告诉我们一张表就是一个独立的对象,也就是说一张表只表达一个意思

3NF 在满足 2NF 的同时,对任何非主属性都不传递依赖于候选键。也就是说不能存在非主属性 A 依赖于非主属性 B,非主属性 B 依赖于候选键的情况

我们用球员表举例子,这张表包含的属性包括球员编号、姓名、球队名称和球队主教练。现在,我们把属性之间的依赖关系画出来,如下图所示:

你能看到球员编号决定了球队名称,同时球队名称决定了球队主教练,非主属性球队主教练就会传递依赖于球员编号,因此不符合 3NF 的要求。

如果要达到 3NF 的要求,需要把数据表拆成下面这样:球员表的属性包括球员编号、姓名和球队名称;球队表的属性包括球队名称、球队主教练

BCNF

如果数据表的关系模式符合 3NF 的要求,就不存在问题了吗?我们来看下这张仓库管理关系表:

在这个数据表中,一个仓库只有一个管理员,同时一个管理员也只管理一个仓库。我们先来梳理下这些属性之间的依赖关系。

仓库名决定了管理员,管理员也决定了仓库名,同时(仓库名,物品名)的属性集合可以决定数量这个属性。这样,我们就可以找到数据表的候选键是(管理员,物品名)和(仓库名,物品名)。

然后我们从候选键中选择一个作为主键,比如(仓库名,物品名)。在这里,主属性是包含在任一候选键中的属性,也就是仓库名,管理员和物品名。非主属性是数量这个属性。

那如何判断这张表的范式等级呢?
首先,数据表每个属性都是原子性的,符合 1NF 的要求;其次,数据表中非主属性”数量“都与候选键全部依赖,(仓库名,物品名)决定数量,(管理员,物品名)决定数量,因此,数据表符合 2NF 的要求;最后,数据表中的非主属性,不传递依赖于候选键。因此符合 3NF 的要求。

既然这张表满足了 3NF 的要求,那么它就不存在问题了吗?我们来分析下面的情况:

  1. 增加一个仓库,但是还没有存放任何物品。根据数据表实体完整性的要求,主键不能有空值,因此
    会出现插入异常;
  2. 如果仓库更换了管理员,我们就可能会修改数据表中的多条记录;
  3. 如果仓库里的商品都卖空了,那么此时仓库名称和相应的管理员名称也会随之被删除。

所以,即便数据表符合 3NF 的要求,同样可能存在插入,更新和删除数据的异常情况

那肿么办呢?

首先我们分析下造成这些异常的原因:主属性管理员对于候选键(仓库名,物品名)是部分依赖的关系,这样就有可能导致上面的异常情况。
因此,人们在 3NF 的基础上进行了改进,提出了 BCNF。它在 3NF 的基础上消除了主属性对候选键的部分依赖或者传递依赖关系。

为了满足 BCNF 的要求,我们需要将上面的仓库管理表拆分成下面两张表:

仓库表:仓库名, 管理员。
库存表:仓库名,物品名,数量。

这样就不存在主属性对于候选键的部分依赖或传递依赖

总结

  • 1NF 需要保证表中每个属性都保持原子性;
  • 2NF 需要保证表中的非主属性与候选键完全依赖;
  • 3NF 需要保证表中的非主属性与候选键不存在传递依赖。
  • BCNF 需要保证表中的主属性与候选键不存在部分依赖或者传递依赖

复杂查询

1. 链接查询

  • 在设计表的时候,为了避免数据的冗余,我们往往会将数据分散到多个表中。因此,在我们查询数据的时候,需要连接多个表进行查询
  • SQL92 和 SQL99 连表查询的语法有很大的不同。建议大家采用 SQL99 标准,因为它的层次性更强,可读性也更高。我们也以 SQL99 标准进行讲解。
  • 连接查询大致可以分为 5 种,分别为:交叉连接,等值连接,非等值连接,外连接和自连接

1.1 交叉连接

交叉连接也叫笛卡尔乘积。那什么是笛卡尔乘积呢?wikipedia 对笛卡尔乘积的定义如下:

在数学中,两个集合X和Y的笛卡儿积(英语:Cartesian product),又称直积,在集合论中表示为X×Y,是所有可能的有序对组成的集合,其中有序对的第一个对象是X的成员,第二个对象是Y的成员。

在 SQL99 中,我们可以通过 CROSS JOIN 获取多张表的笛卡尔乘积。

create database nba;
use nba;
show tables;
select * from player;
select * from team;
select * from player cross join team
#team_id不会合并

交叉连接只是获取所有数据的集合,其中大多数数据都是没意义的,所以我们还需要对数据进行筛选。交叉连接是所有其它连接的基础

1.2 等值连接

  • 等值连接就是对多张表中相同的字段进行等值判断。在 SQL99 中可以有多种方式表示等值连接
1.2.1 自然连接
  • NATURAL JOIN 会自动帮你查询两张连接表中所有相同的字段,然后进行等值连接
# a. 自然连接
select * from player natural join team;//会自动筛选team相等的元素
#team_id会合并

1.2.2 USING连接
  • 还可以用 USING 来指定用哪些同名字段进行等值连接
# b. using连接
select * from player join team using(team_id);
#team_id会合并

1.2.3 ON连接
  • ON 表示我们想要连接的条件,我们也可以用 ON 来实现等值连接
  • 可以实现等值连接,也可以实现非等值连接
# c. on连接
select * from player join team on player.team_id = team.team_id;
#team_id不会合并

1.3 非等值连接

  • 连接两张表的条件如果是相等判断,那就是等值连接,否则就是非等值连接。

比如说:我们想查询每个球员的身高级别

select * from player;
select * from height_grades;select * from player cross join height_grades;select player_id, player_name, height, height_level
from player join height_grades
on height between height_lowest and height_highest;

1.4 外连接

  • 外连接除了查询满足条件的记录以外,还可以查询某一方不满足条件的记录。两张表做外连接,会有一张表是主表,另一张表是从表
1.4.1左外连接
  • 左外连接,就是左边的表是主表,需要显示左边表的全部行。右边表是从表,只显示满足条件的行。关键字为 LEFT OUTER JOIN
create database mydb1;
use mydb1;
create table girls (id int primary key auto_increment,name varchar(255),bf_id int
);
insert into girls values (null, 'Allen', 1);
insert into girls values (null, 'Beyonce', 2);
insert into girls values (null, 'Cindy', 100);
insert into girls values (null, 'Diana', null);create table boys (id int primary key auto_increment,name varchar(255),gf_id int
);
insert into boys values (null, 'Algebra', 1);
insert into boys values (null, 'Bill', 2);
insert into boys values (null, 'Chris', 100);
insert into boys values (null, 'David', null);select * from girls left join boys
on girls.bf_id = boys.id;# 外连接,不满足的也会显示select * from girls join boys#此处join实际为inner join
on girls.bf_id = boys.id;#内连接,不满足的不会显示


1.4.2右外连接
  • 右外连接,就是右边的表是主表,需要显示右边表的全部行。左边表是从表,只显示满足条件的行。关键字为 RIGHT OUTER JOIN
select * from girls right join boys
on boys.gf_id = girls.id;

1.4.3全外连接
  • 两张表都是主表,都需要显示全部行。但是MySQL不支持全外连接。关键字为 FULL OUTER JOIN
# 全外连接
select * from girls full join boys
on boys.gf_id = girls.id;

1.5 自连接

  • 我们可以连接不同的表,也可以对同一张表进行连接,这样的连接我们称之为自连接。

比如我们想要查看比布雷克-格里芬高的球员都有谁

select * from player;
select a.player_id, a.player_name, a.height from player as a join player as b
on b.player_name = '布雷克-格里芬'
where a.height > b.height;

2.联合查询

  • 我们可以用 UNION 关键字,将多个结果集合并成一个结果集,这样的查询我们叫联合查询。
  • 应用场景: 要查询的结果来自多个表,且多个表没有直接的连接关系,但查询的信息一致时。
  • 注意事项:
    a. 列数一致
    b. 对应的数据最好一致
    c. UNION会去重, UNION ALL不会去重
select id, name from girls
union
select id, name from boys;# 用子查询
select * from player as a
where height > (select height from player as b where player_name = '布雷克-格里芬');

3.子查询

  • 子查询其实就是嵌套在查询中的查询。这样做的好处是:我们可以进行更加复杂的查询,更容易理解查询的过程。很多情况下,我们无法直接从数据表中得到我们想要的结果。往往需要先进行一次查询,然后在这次查询的基础上,再次进行查询

子查询可以分为关联子查询非关联子查询

如果子查询只执行一次,然后子查询的结果集会作为外部查询的条件进行判断,那么这样的子查询叫做非关联子查询。比如:我们想要查询哪个球员的身高最高,最高身高是多少

use nba;
# 子查询
# 非关联子查询
# 比如:我们想要查询哪个球员的身高最高,最高身高是多少?
# ① 查询最高身高是多少
select max(height) from player;
# ② 查询身高等于最高升高的球员
select player_id, player_name, height from player
where height = (select max(height) from player);  # 标量子查询 scalar

如果子查询依赖于外部查询,通常情况下是因为子查询用到了外部查询的某些字段。因此,每执行一次外部查询,子查询都要重新执行一次,这样的子查询叫做关联子查询。比如:我们想要查询每个球队中大于平均身高的球员有哪些,并显示球员姓名,身高以及所在球队 ID

# 关联子查询
# 比如:我们想要查询每个球队中大于平均身高的球员有哪些,
# 并显示球员姓名,身高以及所在球队 ID。
select player_name, height, team_id from player as a
where height > (select avg(height) from player as b where b.team_id = a.team_id);

3.1 EXISTS 子查询

  • 关联子查询可能会搭配 EXISTS 关键字一起使用。 EXISTS 用来判断子查询的结果集是否为空集。如果不为空集返回 True ,如果为空集返回 False
# Exists
# 举个列子:查询出过场的球员都有哪些,并显示他们的球员ID,球员姓名,球队ID。
# ① 涉及哪些表?
# select * from player;
# select * from player_score;
select player_id, player_name, team_id
from player
where exists (select player_id from player_score where player_score.player_id = player.player_id
);
  • 那么, NOT EXISTS 自然就是不存在的意思。比如:查询没出过场的球员都有哪些,并显示他们的球员ID,球员姓名,球队ID
# 比如:查询没出过场的球员都有哪些,并显示他们的球员ID,球员姓名,球队ID。
select player_id, player_name, team_id
from player
where not exists (select player_id from player_score where player_score.player_id = player.player_id
);

3.2 集合比较子查询

集合比较子查询的作用是与外部查询的结果集进行比较。主要有以下几个关键字:IN, SOME (ANY),ALL。他们的含义如下:

# IN
# 查询出过场的球员都有哪些,并显示他们的球员ID,球员姓名,球队ID。
select player_id, player_name, team_id
from player
where player_id in (select distinct player_id from player_score
);

了解了 IN 关键字后,我们接下来看下 SOME 和 ALL 。它们都需要和比较操作符一起使用,这些比较操作符包括: > , = , < , >= , <= , 和 <> 。

举个例子:我们要查询比印第安纳步行者 (team_id=1002) 中某个球员身高高的球员有哪些,显示它们的球员ID,球员姓名和球员身高。

# SOME(ANY)
# 我们要查询比印第安纳步行者 (team_id=1002) 中某个球员身高高的球员有哪些,
# 显示它们的球员ID,球员姓名和球员身高。
select player_id, player_name, height
from player as a
where team_id != 1002 and height > SOME(select heightfrom player as bwhere b.team_id = 1002
);select player_id, player_name, height
from player as a
where team_id != 1002 and height > (select min(height)from player as bwhere b.team_id = 1002
);

同样,如果我们想查询比印第安纳步行者 (team_id=1002) 中所有球员身高都高的球员有哪些,显示它们的球员ID,球员姓名和球员身高

# 同样,如果我们想查询比印第安纳步行者 (team_id=1002) 中所有球员身高都高的球员有哪些,
# 显示它们的球员ID,球员姓名和球员身高.
select player_id, player_name, height
from player as a
where height > ALL(select heightfrom player as bwhere b.team_id = 1002
);select player_id, player_name, height
from player as a
where height > (select max(height)from player as bwhere b.team_id = 1002
);

最后,再强调下 SOME 和 ALL 必须要与一个比较操作符一起使用,不然起不到集合比较的作用

3.3 子查询作为计算字段

子查询甚至可以作为计算字段存在。举个例子:查询每个球队的名称,和它们的球员数

# 子查询作为计算字段存在
# 查询每个球队的名称,和它们的球员数
# ① 涉及哪些表? team, player
select team_id, team_name, (select count(*) from player where player.team_id = team.team_id) as player_num
from team;# 连接查询
# ① 查询每个球队的球员数目 select team_id, count(*) from player group by team_id;# ② 连接team表查询 select * from team  left join (select team_id, count(*) as player_num from player group by team_id) as tmpusing(team_id);

Phase2 Day19 数据库范式设计 复杂查询相关推荐

  1. MYSQL之数据库设计范式和高级查询

    文章目录 1 数据库设计范式 范式一 范式二 范式三 反范式 高级查询 基础查询 条件查询 范围查询 判空查询 模糊查询 分页查询 查询后排序 聚合查询 分组查询 1 数据库设计范式 为了建立冗余较小 ...

  2. 数据库范式的思考以及数据库的设计

    数据库范式--通俗易懂[转] 数据库范式是数据库设计中必不可少的知识,没有对范式的理解,就无法设计出高效率.优雅的数据库.甚至设计出错误的数据库.而想要理解并掌握范式却并不是那 么容易.教科书中一般以 ...

  3. MySQL数据库(三):数据库设计与查询语句

    MySQL数据库:数据库设计与查询语句 1.1 目录 文章目录 1.1 目录 1.2 数据库基本概念 1.3 实体和实体之间的关系 1.3.1 一对多 1:N 1.3.2 一对一(1:1) 1.3.3 ...

  4. MySQL - 数据库表设计 - 范式

    目录 一.数据库设计的重要性 二.范式 - 简介: 1.什么是范式? 第一范式 - 单一列 第二范式 - 中间表 - 一对多 第三范式 - 不产生中间表 - 一对一.多对一 三.数据库表设计的注意要点 ...

  5. 数据库逻辑设计之三大范式,一看就懂

    数据库逻辑设计之三大范式 第一范式 第二范式 第三范式 反范式化 范式化设计和反范式化设计的优缺点 范式化 反范式化 第一范式 1NF是对属性的原子性,要求属性具有原子性,不可再分解: 表:字段1. ...

  6. 数据库表设计_3大范式

    为了建立冗余较小.结构合理的数据库,设计数据库时必须遵循一定的规则.在关系型数据库中这种规则就称为范式.范式是符合某一种设计要求的总结.要想设计一个结构合理的关系型数据库,必须满足一定的范式. 在实际 ...

  7. 数据库表设计三大范式

    什么是三大范式? 设计表的依据:按照三大范式设计的表不会出现数据冗余. 三大范式有哪些? 第一范式 :任何表都有一个主键,并且每一个字段的原子性不可再分. 例子:不满足第一范式 学生编号 学生姓名 联 ...

  8. 如何在数据库三范式的基础上进行数据库冗余设计

    数据库设计过程中不仅要考虑遵循第三范式,还要考虑是否冗余 很多数据库设计书籍都强调数据库设计三范式,而三范式的一个重要工作就是消除冗余,可以消除冗余在大多数情况下是正确的.当在实际的业务模型中,处理复 ...

  9. mysql数据库实验3查询_MySQL数据库实验:任务三 数据库的单表查询设计

    任务三 数据库的单表查询设计 文章目录任务三 数据库的单表查询设计[实训目的与要求][实训原理][实训步骤]一.简单查询二.按条件查询1.比较大小查询2.带in关键字的查询(确定集合)3.带BETWE ...

最新文章

  1. 三目运算符字符串拼接
  2. CentOS7 部署 galera cluster mariadb 10.1
  3. win10 修改软件、应用、游戏安装的默认目录
  4. 阿里云cloudmonitor服务导致CPU暴增的异常
  5. 怎么使用Vegas制作炫彩灯光效果?
  6. Kafka配置3--Windows下配置Kafka集群
  7. 将Matplotlib嵌入wxPython的GUI界面中
  8. GIS案例练习-----------第九天
  9. kali 安装netspeed 网络流量监视器
  10. 分享133个ASP源码,总有一款适合您
  11. win10 此电脑中【设备和驱动器】位置出现空白图标
  12. sql2005 查询分析器 自动换行
  13. 华科_图形学笔记_05_初探造型技术_02
  14. 淘宝2011春季校园招聘笔试试题(答案+个人解析版)
  15. 车辆维修管理系统mysql_汽车维修业务管理软件
  16. Python入门教程NO.5 用python写个自动选择加油站的小程序
  17. Robotstudio软件:机床上下料工作站机器人主逻辑编写与仿真运行
  18. 中国计算机软件行业分析2---国内企业分类
  19. Labview读取Excel文件方法
  20. 泡MM的android小程序

热门文章

  1. 计算机设备管理器驱动,什么我的电脑设备管理器里没有非即插即用驱动程序着一项...
  2. 云POS连锁版收银系统免费试用 超市连锁收银软件免费注册
  3. 循环神经网络(RNN)简单介绍及实现(基于表面肌电信号)
  4. Flutter plugin not installed this adds Flutter specific functionality
  5. nodejs下载及安装(windows)
  6. html表单调用js文件上传,简单实现js上传文件功能
  7. mysql中拼接字符串
  8. 利用CMD合并多个VOB文件
  9. 利用光纤通信技术解决社会问题调研报告
  10. 700Gddos高防ip可以防御多少ddos cc攻击