具有多列的单个固定表与灵活的抽象表
Posted
技术标签:
【中文标题】具有多列的单个固定表与灵活的抽象表【英文标题】:single fixed table with multiple columns vs flexible abstract tables 【发布时间】:2011-04-30 01:03:03 【问题描述】:我想知道您是否有一个网站,其中包含需要不同字段的十几种不同类型的列表(商店、餐厅、俱乐部、酒店、活动),创建具有这样定义的列的表是否有好处 示例商店:
shop_id | name | X | Y | city | district | area | metro | station | address | phone | email | website | opening_hours
或者类似这样的更抽象的方法:
object_id | name
---------------
1 | Messy Joe's
2 | Bate's Motel
type_id | name
---------------
1 | hotel
2 | restaurant
object_id | type_id
---------------
1 | 2
2 | 1
field_id | name | field_type
---------------
1 | address | text
2 | opening_hours | date
3 | speciality | text
type_id | field_id
---------------
1 | 1
1 | 2
2 | 1
2 | 3
object_id | field_id | value
1 | 1 | 1st street....
1 | 3 | English Cuisine
当然,如果值是预定义的,它可以更抽象(例如:专业可以有自己的列表)
如果我采用抽象方法,它可能会非常灵活,但查询会更复杂,有很多连接。 但我不知道这是否会影响性能,执行这些“更复杂”的查询。
我很想知道这两种方法的优缺点是什么。我可以自己想象,但我没有经验来证实这一点。
【问题讨论】:
谢谢。你可能也对这个question/answer感兴趣。 【参考方案1】:某些问题需要澄清和解决,我们才能进行合理的讨论。
先决条件
标签 在要求精确的职业中,使用精确的标签很重要,以避免混淆,这样我们就可以在不必使用冗长的描述和限定词的情况下进行交流。
您发布为 FixedTables 的内容是未标准化的。很公平,这可能是第三范式的尝试,但实际上它是一个平面文件,非规范化(不是“非规范化”)。准确地说,您发布为 AbstractTables 的是 Entity-Attribute-Value,这几乎是但不完全是第六范式,因此比 3NF 更规范化。当然,假设它是正确的。
未规范化的平面文件不是“非规范化”的。它充满了重复(没有做任何事情来删除重复的组和重复的列或解决依赖关系)和 Null,它在许多方面都是性能消耗,并且会阻止并发。
为了进行非规范化,必须首先对其进行规范化,然后由于某些充分的理由使规范化稍微后退。由于它一开始没有被规范化,因此不能被非规范化。它只是非规范化的。
不能说它是“为了性能”而去规范化的,因为作为性能狂,它是性能的对立面。好吧,他们需要为缺乏形式化设计提供一个理由],而“为了性能”就是这样。即使是最小的正式审查也暴露了虚假陈述(但很少有人能提供,所以它一直被隐藏起来,直到他们让外人来解决,你猜对了,巨大的性能问题)。
规范化结构的性能远优于非规范化结构。更多归一化结构 (EAV/6NF) 比归一化更少的结构 (3NF/5NF) 表现更好。
我同意 OMG Ponies 的主旨,但不同意它们的标签和定义
而不是说'除非你必须'不要“去规范化”',我说的是,'忠实地规范化,句号'和'如果有是性能问题,你没有正确规范化'。
*** 范式和范式的条目提供了不正确的定义;他们混淆了范式;他们缺乏规范化的过程;他们对很久以前就被揭穿的荒谬或有问题的 NF 给予同等重视。结果是,***增加了一个已经混乱且很少被理解的主题。所以不要浪费你的时间。
但是,为了进步,在没有那个参考构成障碍的情况下,让我这样说。
3NF 的定义是稳定的,没有改变。 3NF 和 5NF 之间的 NF 存在很多混淆。事实上,这是一个在过去 15 年中取得进展的领域。许多组织、学者和供应商的产品受到限制,纷纷创建一个新的“标准表格”来验证他们的产品。所有服务于商业利益和学术不健全。 3NF 在其原始未篡改状态下旨在并保证某些属性。 总而言之,5NF 就是今天,3NF 是 15 年前的样子,你可以跳过商业玩笑和中间的十二个左右“特殊”(商业和伪学术)NF,一些其中在 Wikipedia 中被标识,甚至以令人困惑的术语。第五范式 既然您已经能够在您的帖子中理解和实施 EAV,那么您将毫无问题地理解以下内容。当然,真正的关系模型是先决条件,强键等。第五范式是,因为我们跳过第四:
第三范式 简而言之,每个表中的每个非键列都与表的主键具有 1::1 的关系, 并且没有其他非关键列 零数据重复(如果规范化是努力推进的结果;不是靠智慧或经验来实现的,也不是在没有正式流程的情况下将其作为目标而努力实现的) 没有更新异常(当您更新某处的列时,您不必更新位于其他地方的同一列;该列仅存在一个位置)。 如果您了解上述内容,可以忽略 4NF、BCNF 和所有愚蠢的“NF”,它们是物理化记录归档系统所必需的,正如学术界所提倡的,与关系模型 (Codd) 完全不同。李>第六范式
目的是消除缺失数据(属性列),也就是消除 Nulls 这是 Null 问题(也称为处理缺失值)的一个真正解决方案,其结果是一个没有 Null 的数据库。 (可以使用标准和 Null 替代在 5NF 上完成,但这不是最佳选择。)如何解释和显示缺失值是另一回事。 从技术上讲,它不是一个真正的范式,因为它没有 5NF 作为先决条件,但它有一个值EAV 与第六范式 我写的所有数据库,除了一个,都是纯 5NF。我已经使用(管理、修复、增强)几个 EAV 数据库,并且我已经实现了许多真正的 6NF 数据库。 EAV 是 6NF 的松散实现,通常由对规范化和 NF 没有很好掌握但可以看到 EAV 的价值并需要其灵活性的人完成。你就是一个很好的例子。
不同之处在于:因为它是松散的,并且因为实现者没有要忠实的引用(6NF),所以他们只实现他们需要的东西,并且全部用代码编写;最终成为一个不一致的模型。
然而,纯粹的 6NF 实现确实有一个纯粹的学术参考点,因此它通常更严格、更一致。通常这会显示在两个可见元素中:
6NF 有一个包含元数据的目录,所有内容都在元数据中定义,而不是在代码中。 EAV 没有,一切都在代码中(实现者跟踪对象和属性)。显然,目录简化了列的添加、导航,并允许形成实用程序。 6NF 被理解后,为空问题提供了真正的解决方案。 EAV 实现者,因为他们没有 6NF 上下文,处理代码中丢失的数据,不一致,或者更糟,允许数据库中的空值。 6NF 实现者不允许 Null,并且一致且优雅地处理缺失数据,而不需要代码构造(对于 Null 处理;当然,您仍然需要为缺失数据编写代码)。例如。对于具有目录的 6NF 数据库,我有一组 procs 将 [重新] 生成执行所有 SELECT 所需的 SQL,并且我为所有用户提供 5NF 视图,因此他们不需要知道或理解底层 6NF 结构.他们被赶出目录。因此,更改很容易且自动化。由于没有目录,EAV 类型手动执行此操作。
讨论
现在,我们可以开始讨论了。
"当然可以更抽象,如果 值是预定义的(例如: 专业可以有自己的 列表)”
当然。但不要太“抽象”。保持一致性并以与其他列表相同的 EAV(或 6NF)方式实施此类列表。
"如果我采用抽象的方法 可以非常灵活,但查询会 有很多连接会更复杂。 但是不知道会不会影响 性能,执行这些“更多 复杂的查询。”
联接在关系数据库中是行不通的。问题不在于数据库,问题在于SQL在处理连接时很麻烦,尤其是复合键。
EAV 和 6NF 数据库有更多的 Joins,就像行人一样,不多也不少。如果您必须手动编写每个 SELECT 代码,当然,繁琐变得非常繁琐。
整个问题可以通过 (a) 使用 6NF over EAV 和 (b) 实现一个目录,您可以 (c) 从中生成所有基本 SQL。也消除了一整类错误。
一个普遍的神话是,连接是有代价的。完全错误。
连接是在编译时实现的,没有任何实质内容会“消耗”CPU 周期。 问题是要连接的表的大小,而不是这些相同表之间的连接成本。 在正确的 PK⇢FK 关系上连接两个表,每个表都有数百万行,每个表都有适当的索引 (在父 [PK] 端唯一;在子端唯一 [PK=parent FK + something] 是瞬时的 如果 Child 索引不是唯一的,但至少前导列是有效的,则速度较慢;在没有有用索引的地方,当然很慢。 与加入费用无关。 返回多行的地方,瓶颈是网络和磁盘布局;不是连接处理。因此你可以随心所欲地“复杂”,没有成本,SQL可以处理它。
我很想知道是什么 两种方法的优缺点。 我可以自己想象,但我 没有经验确认 这个。
就实施而言,5NF(或未取得进展的人为 3NF)是最简单和最好的;易用性(开发人员和用户);和维护。
缺点是,每增加一列,就得改变数据库结构(表DDL)。这在某些情况下很好,但在大多数情况下并非如此,因为变更控制到位,相当繁重。 其次,您必须更改现有代码(处理新列的代码不计在内,因为这是必要的):在实施良好标准的情况下,将其最小化;如果它们不存在,则范围是不可预测的。EAV(这是您发布的内容)允许在不更改 DDL 的情况下添加列。这是人们选择它的唯一原因。 (处理新列的代码不计算在内,因为这是必要的)。如果实施得好,不会影响现有代码;如果没有,它会的。
但您需要具备 EAV 能力的开发人员。
当 EAV 实施不当时,它是可恶的,比 5NF 做得不好更糟糕,但并不比非规范化更糟糕,这是大多数数据库所采用的(被歪曲为“为了性能而去规范化”)。 当然,保持强大的事务上下文(比在 5NF/3NF 中)更重要,因为列的分布更加分散。 同样,保留声明性引用完整性也很重要:我所看到的混乱在很大程度上是由于开发人员删除了 DRI,因为它变得“难以维护”,结果正如您想象的那样,一个母亲具有重复的 3NF/5NF 行和列的数据堆。以及不一致的 Null 处理。假设服务器已针对预期目的进行了合理配置,则性能没有差异。 (好吧,有一些特定的优化只能在 6NF 中实现,而在其他 NF 中是不可能的,但我认为这超出了这个线程的范围。)同样,EAV 做得不好会导致不必要的瓶颈,仅此而已未归一化。
当然,如果您选择 EAV,我建议您更正式一些;全额购买;选择 6NF;实施目录;生成 SQL 的实用程序;意见;一致地处理缺失数据;完全消除 Null。这减少了您对开发人员质量的脆弱性;他们可以忘记 EAV/6NF 深奥的问题,使用视图,专注于应用逻辑。
【讨论】:
哇,感谢您的大量响应,非常有趣。当然,我需要重新阅读它几次,但我想问一下,学习如何掌握 6NF 最可靠的资源是什么?***和谷歌的结果没有那么有用。你是从哪里/怎么学的? 谢谢你的客气话。没有这样的来源。有很好的教科书。网络上可用的信息(对于任何东西,不仅仅是这个狭窄的主题)都是垃圾 Wiki 是一项平庸的研究。你得到你拍的东西。从一个好的大学获得正式的 IT 学位是最好的起点。众所周知,精通来自与大师的合作。 AFAIK 只有一个其他合作伙伴提供了对这一主题的这种掌握程度:他们将其作为产品出售;我将它作为一种服务出售,因为我相信客户真的必须了解它,拥有它,而不是将它锁定在产品中。 在哪里/如何。好吧,我做到了以上所有,我非常感谢我所拥有的优秀老师。我每年改进大约四个数据库,对于大型银行,这是我的热情/专业。如果我在这里多说,那就太不礼貌了。如果您对更多细节感兴趣,请追我:个人资料⇢网站⇢电子邮件。我很乐意回答你的问题。干杯。 您是否考虑过修复***页面?它会帮助很多很多人去那里获得信息的第一个近似值并因此感到非常困惑。 @Divide。早年,我曾经修复过 wiki。但我很快就厌倦了一遍又一遍地修复它。这是一个污水池,任何人,无论权威与否,对与否,都可以编辑。它是群众“贡献”的,出于他们的政治目的,在权威信息方面没有什么可提供的。为此,一个人需要正规的高等教育。而这目前正在被销毁。【参考方案2】:在您的问题中,您至少同时提出了两个主要问题。这两个问题是 E-A-V 和 gen-spec。
首先,让我们谈谈 E-A-V。您的最后一张表(object_id、field_id、value)本质上是一个 E-A-V。 E-A-V 有一个优点,E-A-V 有一个缺点。好处是结构非常通用,几乎可以容纳描述几乎任何主题的任何数据体。这意味着您可以在不进行数据分析和不了解主题的情况下进行设计和实施,而不必担心错误的假设。不利的一面是,在检索时,您必须进行在构建数据库之前跳过的数据分析,以便提出有意义的查询。这比仅仅检索效率要严重得多。但是你也会在检索效率方面遇到可怕的问题。只有两种方法可以了解这个陷阱:通过它或从那些有过它的人那里了解它。我推荐阅读。
其次,您有一个 gen-spec 案例。您的表 (object_id, type_id) 捕获 gen-spec (generalization-specialization) 模式以及相关表。如果我必须在酒店和餐馆之间进行概括,我可能会称之为“公共住宿”或“场所”。但我不确定我是否理解你的情况,你可能会驾驶比这两个名字所暗示的更普遍的东西。毕竟,您已经在列表中包含了“活动”,而在我看来,活动并不是一种场所。
在之前的回复中,我已向其他人推荐过有关 gen-spec 和关系模型的阅读材料。When two tables are very similar, when should they be combined?
但我很犹豫是否要让您朝同一个方向走,因为我不清楚您是否想在构建数据库之前提出数据的关系模型。数据体的关系模型和相同数据的 E-A-V 模型几乎完全不一致。在我看来,您必须在探索如何在数据关系模型中表达 gen-spec 之前做出选择。
【讨论】:
【参考方案3】:当您开始需要大量不同的实体时(甚至之前...),nosql 解决方案将比任何一种选择都简单得多。 只需使用您需要的确切字段存储每个实体/记录。
"id": 1,
"type":"Restaurant",
"name":"Messy Joe",
"address":"1 Main St.",
"tags":["asian","fusion","casual"]
【讨论】:
【参考方案4】:“抽象”方法更好地称为“规范化”,看起来像第 3 范式 (3NF)。
另一个称为“非规范化”,可以作为有效的性能选项...当您使用规范化方法遇到速度问题时,而不是以前。
【讨论】:
所以你的意思是应该建立规范化的方法,如果性能确实是一个问题,那么升级硬件或更改所有代码并创建新表?抱歉,我不确定你在告诉我什么...... @Moak:是的。在不需要的情况下进行非规范化方法是过早的优化。 由于我不知道这些范式,您能否建议我是否应该研究一下第一个、第二个、第四个、第五个或其他范式? @Moak: Wikipedia has a good primer【参考方案5】:您如何在代码中显示列表?我猜Listing
是超类型,Shop
、Restuarant
等是子类型?
假设是这样,这是一个如何将子类型映射到关系数据库的案例。一般有三种选择:
选项 1:每个子类型一个表, 具有重复的共同属性 每个表(名称、ID 等)。 选项 2:所有对象的单个表(您的单个表方法) 选项 3:超类型的表和每个子类型的表没有普遍正确的解决方案。我的偏好通常是从选项 3 开始;它提供了一个直观的结构来使用,非常规范化并且可以很容易地扩展。这意味着用于检索每个实例的单个连接 - 但 RDBMS 已针对连接进行了很好的优化,因此在实践中它不会真正导致性能问题。
选项 2 对于查询(无连接)可能更高效,但如果其他表需要引用所有超类型实例(外键扩散),则会导致问题。
选项 1 乍一看似乎是性能最高的,但有 2 个警告:(1) 它不具备弹性改变。如果添加新的子类型(以及不同的属性),则需要更改表结构并迁移它。 (2) 它可能没有看起来那么有效。由于表的数量很少,一些数据库的存储效率并不高。因此,它的效率可能低于选项 1 - 因为查询引擎执行连接的速度比搜索膨胀的稀疏表空间的速度要快。
选择哪一个真正归结为了解您的问题的细节。我建议阅读一下选项:this article 是一个不错的起点。
第
【讨论】:
以上是关于具有多列的单个固定表与灵活的抽象表的主要内容,如果未能解决你的问题,请参考以下文章
数据结构-线性表(顺序表与链表的基本知识 以及ArrayList 源码分析)
从单个实体迁移到具有子实体的抽象父实体,未调用 NSEntityMigrationPolicy