数据规范化和编写查询

Posted

技术标签:

【中文标题】数据规范化和编写查询【英文标题】:Data normalization and writing queries 【发布时间】:2011-09-19 23:46:32 【问题描述】:

我是小学生。开发人员(工作 5 个月),我有一个关于数据规范化的问题。现在,据我了解,数据规范化背后的一般原则是创建一个 RDBMS,将​​数据冗余保持在最低限度。在我的项目中,其中一位 DB 人员创建了一个 DB。我们有 50 多个表,数据库中的表通常非常零散,即。一张表有两三列,就是这样。现在,在编写 sql 查询时,它已成为一件小事,因为每个查询都涉及梳理几个不同的表并将它们连接在一起。我想知道这是否是数据规范化的副作用?或者这是否指向别的东西?

我知道,对我来说,最简单的事情就是根据我必须编写的查询来编写表。这将创建一个包含大量冗余数据的数据库,但我很好奇是否有一个快乐的媒介?

就像附言一样,我不想让人觉得我在抱怨我的工作,但我真的很想了解更多关于这方面的信息。我的工作环境不是最友好的,所以我不愿意和同事提出这个问题。但是,如果有经验丰富的人提供任何想法、书籍、教程或意见,我将不胜感激。

谢谢。

【问题讨论】:

正如你所说。这是数据规范化的副作用。查询通常更难编写,但您可以在最小冗余、减少数据修改异常、更快的搜索、更快的排序等方面获得巨大的收益。 很难回答这样一个笼统的问题,而不知道为什么数据如此分散。假设数据没有严重规范化,通常您会在非规范化状态下创建 TABLEs,然后使用 VIEWs 保存常用查询以供重复使用。 看到这个问题:***.com/questions/1724381/… 还有这个:***.com/questions/246701/… 谢谢。我可能会选择观景路线。 【参考方案1】:

现在,据我所知,一般 数据规范化背后的原则是 创建一个RDBMS,其中数据 冗余被保持在最低限度。

嗯,好的。

在我的项目中,一位 DB 人员 创建了一个数据库。我们有 50 多张桌子,并且 数据库中的表通常非常 支离破碎,即一张桌子有两个或 三列就是这样。

桌子的数量并不能说明设计的好坏。有些企业需要一两个。其他人需要更多。我曾在财富 500 强的数据库中工作过,其中包含数千个表。

列数并不能说明设计的好坏。并且列数与碎片无关。我会说列相对较少的表通常是一个好兆头。并不总是一个好兆头,但通常是一个好兆头。

现在,说到编写 sql 查询,它已成为一种 小麻烦,因为每个查询都涉及 梳理了几种不同的 表并将它们连接在一起。一世 想知道这是不是一面 数据标准化的影响?或者确实 这指向别的东西?

这有两个不同的常见原因。

当您对表进行规范化时,您可以通过识别函数依赖关系、隔离一个或多个新表中的函数依赖列并将它们从原始表中删除来减少冗余(并提高数据完整性)。所以规范化一个表格,从一个较低的范式移动到一个较高的范式

总是增加 表, 总是减少列数 在原始表格中,并且 有时需要加入才能检索 人类数据。

另一种常见的做法是用 id 号替换字符串。这与标准化无关。 (没有“id number normal form”之类的东西。)用 id numbers 替换字符串

总是增加 表, 不改变列数 在原始表中(除非在 与归一化同时), 总是需要一个连接来为人类检索数据。

此线程的其他部分似乎有些混乱。我意识到,严格来说,以下内容均直接与 OP 的问题相关。

1NF 是“一值”原则。它与 row 是“原子的”没有任何关系。在关系模型中,atomic 不指代行;它指的是价值观。

“一个值”表示行和列的每个交集都包含一个值。 (换句话说,值是“原子的”。但是atomic这个词有一些不幸的含义,所以大多数现代从业者都避免使用它。)这个值不需要简单;它可以任意复杂。但是,如果它有自己有意义的部分,dbms 要么完全忽略这些部分,要么提供操作它们的函数。 (不必编写函数来操作部件。)

我认为最简单的例子是日期。日期有部分,由年、月和日组成。 dbms 要么忽略这些部分(如SELECT CURRENT_DATE),要么提供操作它们的函数(如SELECT EXTRACT(YEAR FROM CURRENT_DATE))。

试图回避“一个值”原则会导致一个推论:“无重复组”原则。

重复组包含来自一个域的多个值,所有值具有相同的含义。所以像下面这样的表是一种重复组的例子。 (还有其他类型。)“phone_1”和“phone_2”的值来自同一个域,并且它们具有相同的含义——用户“n”具有电话号码(phone_1 和 phone_2)。 (主键是“user_id”。)

user_id    phone_1           phone_2    
1          (111) 222-3333    (111) 222-3334
2          (111) 222-3335    (111) 222-3336

但下一张表虽然非常相似,但没有重复组。这些值来自同一个域,但它们的含义不同。 (主键是“user_id”。)

user_id    home_phone        work_phone    
3          (111) 222-3333    (111) 222-3334
4          (111) 222-3335    (111) 222-3336

2NF 是“全键”原则。它与键的数量无关;具有“n”列的表可能有“n”个键。 (例如,参见this other SO answer。)在关系模型中(以及,当您进行规范化练习时),如果您看到单词 key 本身,请认为“候选键”。

相反,2NF 与具有多列的候选键有关。当候选键有多个列时,2NF 要求每个非主属性在功能上依赖于每个候选键的所有列,而不仅仅是任何候选键的某些列。 (非主属性是不属于任何候选键的属性。)

以下示例改编自Wikipedia entry on 2nf。 (主键是 employee, Skill。)

Table: employee_skills
employee        skill            current_work_location
--
Jones           Typing           114 Main Street
Jones           Shorthand        114 Main Street
Jones           Whittling        114 Main Street
Bravo           Light Cleaning   73 Industrial Way
Ellis           Alchemy          73 Industrial Way
Ellis           Flying           73 Industrial Way
Harrison        Light Cleaning   73 Industrial Way

虽然非主列 current_work_location 在功能上确实依赖于主键 employee, Skill,但它在功能上也仅依赖于主键的一部分,即“员工”。该表不在 2NF 中。

您无法通过为每一行分配一个代理键来回避 2NF 问题。 (主键是 es_id;前一个主键 employee, Skill 有一个 UNIQUE 约束)。

Table: employee_skills
es_id   employee        skill            current_work_location
--
1       Jones           Typing           114 Main Street
2       Jones           Shorthand        114 Main Street
3       Jones           Whittling        114 Main Street
4       Bravo           Light Cleaning   73 Industrial Way
5       Ellis           Alchemy          73 Industrial Way
6       Ellis           Flying           73 Industrial Way
7       Harrison        Light Cleaning   73 Industrial Way

很明显,添加 id 号并没有消除部分依赖 employee->current_work_location。在不去除部分依赖的情况下,这个表仍然不在2NF中。

3NF 是“无传递依赖”原则。正如您从the Wikipedia example 中看到的那样,它不一定与派生或计算数据有关,在此处改编。 (主键是 tournament, year。此表不在 3NF 中。)

Table: tournament_winners
tournament             year  winner            winner_date_of_birth
--
Indiana Invitational   1998  Al Fredrickson    21 July 1975
Cleveland Open         1999  Bob Albertson     28 September 1968
Des Moines Masters     1999  Al Fredrickson    21 July 1975
Indiana Invitational   1999  Chip Masterson    14 March 1977

两个依赖关系表明该表具有传递依赖关系。

    winner_date_of_birth 中的值 似乎在功能上取决于 首要的关键。每个主键值 确定一个且只有一个值 对于winner_date_of_birth。但 。 . . winner_date_of_birth 中的值 似乎在功能上也依赖于 优胜者。获胜者的每个价值 确定一个且只有一个值 对于winner_date_of_birth。

鉴于这两个明显的功能依赖关系以及对锦标赛获胜者出生日期这三个词的含义的理解,我们可以说那个

获胜者 -> 获胜者日期出生日期是 功能依赖,以及 tournament, year -> 获胜者是一个函数依赖,并且 锦标赛,年份 -> Winner_date_of_birth 是传递的 依赖。

【讨论】:

很遗憾,您获得的赞数少于接受的答案。似乎人们投票给他们看到的第一个不愚蠢的答案。 @Alexander Malakhov:它可能会随着时间而改变。我偶尔会为我几个月前写的东西投票。有时,OP 会更改接受的答案。如果您认为我的答案对规范化的其他问题有用,您可以链接到它而不是接受的答案。 FWIW,我认为规范化比数据库或编程工作的任何其他方面都更容易被误解。【参考方案2】:

数据规范化背后的一般原则是创建一个将数据冗余保持在最低限度的 RDBMS。

只有部分正确。

规范化与“冗余”无关。

这是关于“更新异常”。

1NF 是“不要使用数组”规则。打破 1NF 意味着一行不是原子的,但是集合和集合中的独立更新不会很好地工作。会有锁定和缓慢。

2NF 是“一键”规则。每行只有一个键,行中的所有内容都取决于该键。对密钥的 part 没有依赖关系。有些人喜欢谈论候选键、自然键和外键;它们可能存在,也可能不存在。当所有属性都依赖于一个键时,满足 2NF。如果键是单列代理键,那么这个范式很容易满足。

如果违反了 2NF,则您的列依赖于键的一部分,而不是整个键。如果您有一个以(零件号,修订号)为键的表,以及颜色和重量的属性,其中重量取决于整个键,但颜色仅取决于零件号。您有一个 2NF 问题,您可以更新某些部分颜色但不能更新其他颜色,从而造成数据异常。

3NF 是“唯一关键”规则。如果将派生数据放在一行中,并更改派生结果,它与源列不匹配。如果您更改源列而不更新派生值,您也会遇到问题。是的,触发器是允许违反 3NF 设计的一种不好的解决方法。那不是重点。重点只是定义 3NF 并表明它可以防止更新问题。

每个查询都涉及梳理多个不同的表并将它们连接在一起。我想知道这是否是数据规范化的副作用?

是的。

【讨论】:

+1 但实际上唯一值得一提的正常形式是 BCNF(应该是 3NF)、5NF(“完全规范化”并且总是可以实现的)和 6NF(总是可以实现但并不总是可取的)。 您说“每一行只有一个键”。那是完全错误的。没有一种范式要求关系只有一个键,并且 2NF 不需要计算候选键。 2NF 与非主要属性在功能上是否依赖于每个键的整体(一件好事)或仅依赖于任何键的一部分(坏事,需要进一步规范化)有关。 @S.Lott:当你说“每一行只有一个键”时,你错了。例如,请参阅 Date 的 数据库系统简介,您可能会在其中找到“我们已经看到给定 relvar 有可能拥有多个候选键”的引用。 (第 7 版,第 260 页) 我从未说过候选键或自然键始终是主键。我说你声称“每一行只有一个键”是错误的。具有“n”列的表可以有“n”个候选键。关系模型不要求它们中的任何一个都是外键,也没有提供任何逻辑基础来指定一个候选键为主键,所有其他候选键为辅助键。 (这样做可能有很好的实际原因,但这些原因超出了关系模型的范围。) 请不要被冒犯,我不是要挑剔什么的。但老实说,我相信你答案的 2NF 部分是不正确的。如果您的意思是“主键”,那么 Each row has exactly one primary key 很奇怪,并且在 2 种方面没有意义:1恰好一个)根据主键的定义,可以有只有一个无视任何NF 2每一行)绝对不可能有些行有主键而其他行没有。你同意吗(我稍后会谈到其他观点)?【参考方案3】:

在设计良好的数据库中,查询中所需的连接应该很容易编写代码。缺点是你有冗长的 SQL。好处是巨大的:-

一致且易于更新的表格。 根据业务需求快速更改。设计良好的数据库通常可以处理在原始设计中甚至没有考虑过的查询。 快速适应新实体。将新的数据实体和属性添加到设计良好的数据库中相对容易。对非规范化数据库进行看似简单的更改可能是一场噩梦。

【讨论】:

【参考方案4】:

Database views 是解决这一困境的一个至关重要的工具。 This excellent introduction 说:

这是个好消息:您不必使用规范化表! ...在顶部创建连接视图的抽象层非常容易(至少对于 DBA 而言)规范化数据表,将基表完全“隐藏”在视线之外。

【讨论】:

【参考方案5】:

如果没有看到数据,很难说您的数据是否过度规范化(或者只是没有正确规范化 - 将字段分散到多个表中并不意味着它已规范化)一般来说,您可能需要加入几个表以查看规范化数据库中的有用数据。

您可以创建将表连接在一起的视图,然后您可以查询该视图。这在选择数据时可能会有所帮助。

【讨论】:

@onedaywhen 怎么样而不是尖刻的评论,你增加了一些价值。您从未听说过过度规范化的想法,或者您认为这不可能?在您自己的评论中,您提到 6NF 总是可以实现的,但并不总是可取的。如果你在不理想的情况下达到了 6NF,那会不会被认为是过度标准化? 对不起,我没有详细说明。诸如“良好规范化”和“过度规范化”之类的短语是无用且几乎没有意义的非正式术语。此外,在我看来,您的回答似乎并没有解决主要问题,而@S.Lott 回答的前三句话确实如此(尽管关于 1NF、2NF、3NF 的观点并不真正相关),所以他们得到了支持和你被否决了。我可以发布一个示例,说明 6NF 何时可以实现但不可取,但我认为在这种情况下它不会增加价值,不,“过度标准化”不适用。【参考方案6】:

这听起来像是数据规范化,但我必须更多地了解架构、业务案例等,才能可靠地进行调用。如果您可以控制数据库,则可以编写一个view 来表示链接表的常见查询。为了提高性能,您可以创建一个indexed or materialized 视图(名称取决于数据库平台,在本例中为 Oracle 与 Sql Server)。

几乎所有数据库入门都可以帮助您了解这些概念。如果您正在使用 Sql Server 并且真的有兴趣了解更多信息,SQL Server Books Online 是一个很好的资源。

【讨论】:

【参考方案7】:

拥有大量表绝对是规范化数据库设计的标志。这在编写查询时可能会很痛苦,但比让数据不同步要好得多。

我有时会根据包含数千个表的数据库编写报告。每天晚上,我们都有一个程序运行并将生产表中的数据转储到data warehouse,以便我们可以更轻松地对其进行报告。数据仓库表的规范化程度要低得多,这使得编写查询更加简单。如果对您的情况有意义,您可能需要考虑这样的事情。

【讨论】:

以上是关于数据规范化和编写查询的主要内容,如果未能解决你的问题,请参考以下文章

使用 Spring 规范编写复杂的 SQL 查询

ORM规范: JPA

在 MySQL 中实现 3NF 而不损失查询速度?

团队-及格成绩查询系统-代码设计规范

如何在Firebase中编写非规范化数据

Cloud Firestore 查询是不是仍区分大小写?