【问题标题】:Pros and cons of making database IDs consistent and "readable" [closed]使数据库 ID 一致且“可读”的利弊 [关闭]
【发布时间】:2011-02-09 22:24:06
【问题描述】:

问题

数据库 ID“无意义”是否是一个好的经验法则?相反,以一种一眼就能认出的方式构建 ID 是否有显着的好处?有什么好处和坏处?

背景

我刚刚与我的同事就我们数据库中 ID 的一致性进行了辩论。我们有一个利用 Spring 的数据驱动应用程序,因此我们很少需要更改代码。这意味着,如果出现问题,数据更改通常是解决方案。

我的论点是,通过使 ID 保持一致和可读,我们可以为自己节省大量时间和长期的麻烦。一旦设置了 ID,它们就不必经常更改,如果操作正确,未来的更改将不会很困难。我同事的立场是,ID 永远不重要。将信息编码到 ID 中违反了数据库设计策略,并且保持它们有序需要额外的工作,“我们没有时间去做”。我在网上找不到任何支持这两种立场的东西。所以我要求助于 SA 的所有大师!

示例

想象一下这个表示杂货店食物的简化数据库记录列表,第一组表示在 ID 中编码的数据,而第二组则没有:


ID 的含义:

Type
1 Fruit
2 Veggie

Product
101 Apple
102 Banana
103 Orange
201 Lettuce
202 Onion
203 Carrot

Location
41 Aisle four top shelf
42 Aisle four bottom shelf
51 Aisle five top shelf
52 Aisle five bottom shelf

ProductLocation
10141 Apple on aisle four top shelf
10241 Banana on aisle four top shelf
//just by reading the ids, it's easy to recongnize that these are both Fruit on Aisle 4

ID 没有意义:

Type
1 Fruit
2 Veggie

Product
1 Apple
2 Banana
3 Orange
4 Lettuce
5 Onion
6 Carrot

Location
1 Aisle four top shelf
2 Aisle four bottom shelf
3 Aisle five top shelf
4 Aisle five bottom shelf

ProductLocation
1 Apple on aisle four top shelf
2 Banana on aisle four top shelf
//given the IDs, it's harder to see that these are both fruit on aisle 4

总结

保持 ID 的可读性和一致性有哪些优点和缺点?您通常更喜欢哪种方法,为什么?是否有公认的行业最佳实践?

-------- 编辑( 来自 cmets 的有用背景信息,如下 ):--------

在我们的表中,主键始终是一个包含唯一整数的 ID 字段。起初,这个整数是任意的。随着时间的推移,其中一些 ID 在开发人员/测试人员中自然而然地具有了意义。在最近的一次重构中,某些开发人员还花时间让所有 ID 更易于识别。它让每个人的工作轻松了 100 倍。有些人(实际上并不使用数据/代码)出于理论上的原因强烈反对。在实践中,这些反对意见中没有一个是成立的。此外,所有使用这些数据的开发人员都认为现在维护起来要容易得多。

我正在寻找(但还没有看到)反对在以数据为中心的环境中使用可立即识别的 ID 的站得住脚的论据。

【问题讨论】:

  • 将关系信息编码到 ID 中似乎很愚蠢,因为关系数据库本质上会为您维护这一点。另外,数据库ID不一定是人类可读的。如果您需要易于人类解析的关系数据,则可以构建查询/视图来显示数据,或者考虑使用不同的机制来存储数据。考虑人类可解析信息的唯一真正原因是用于调试 (imo)
  • @Alan:我以前从未做过社区 Wiki。如果我想,我将如何转换这个问题?甚至可以转换吗?
  • @Alan:您已经完全触及问题的核心:如果您将花费大量时间调试数据并且几乎不需要努力使 ID“一致”和“可读, ” 那么这样做真的“邪恶”吗?
  • @gmale 我不认为这个愚蠢的问题可以得出 11 个答案,其中一个得到高度评价。问题中一定有某些东西
  • 请不要破坏您的问题。

标签: database database-design data-driven


【解决方案1】:

缺点:我刚刚将“第五通道顶层货架”更改为“第六通道顶层货架”,所以现在我必须将其 ID 更改为 61,现在我必须将“第五通道顶层货架上的葡萄”的 ProductLocation ID 更改为是 10461 哦,天哪,货架位置 ID 字符串还出现在我数据库中的 ID 中的什么地方哦天哪,无论谁设计了带有意义的 ID ID 为 41 die die die。

【讨论】:

  • @gmale:变量/函数名称(设计为供人类阅读并传递语义信息)和行 ID(不是为人类阅读而设计的,也不传递语义信息)。
  • @gmale:你的解释没有意义。如果您尝试修复数据问题,请编写公开此信息的查询。现在在 id 中编码信息只会加剧问题。如果您愿意,该数据库将帮助您保持完整性。如果您设计自己的元模式,您可能会允许对数据进行目视检查,但您会失去机器验证的能力。
  • @gmale:您还没有解决“41 不再意味着'第 4 架底部'它意味着'第 5 架顶部'”的问题,因此“10141 并不意味着什么”你认为这意味着“问题,因此你必须去货架表找出 41 无论如何意味着什么。确实,听起来您的想法是在数据库中包含语义 ID,而与您交谈的每个人都试图告诉您这不是一个好主意,但您坚信自己是对的。好吧,有时规范就是规范是有原因的。
  • @gmale:一切都不是神奇的“一致”。由您(程序员)强制执行以原子方式更新对更改 ID 的每个引用。数据库引擎根本不会帮助您解决这个问题,尤其是对于其中隐藏着其他 ID 的 ID。
  • @gmale:如果值 can 发生变化,那么我们就回到问题“为什么 101 编码到 ID 中比值 101 (或者只是 1,就此而言)存储在该行的另一列中,因为下次它可能是 102?”人工调试器总是需要交叉引用。
【解决方案2】:

使用数据库 ID 对有关行的信息进行编码存在几个问题。如果您希望胡萝卜的“ID”为 203,则应添加 product_id 列(例如)并将此信息放在那里。为什么?

  1. 通过自定义您的 ID,您必须添加管理您的 ID 的特定于域的代码,并且您不能依赖自动递增或 UUID 等数据库功能。
  2. 如果您必须更改分类,则会弄乱您的表格关系、浏览器书签、搜索引擎结果等。
  3. 这不是常见的做法 - 因此,当您将应用程序或域特定的数据放入 ID 字段时,许多人会认为这是无意义的信息,而实际上并非如此。您将需要一个数据字典(并且您必须确保人们阅读该数据字典)来说明这是有价值的信息这一事实。

ID 唯一需要的用途是唯一标识表中的行。如果它可以提供良好的查找性能,那是一个奖励,如果它可以紧凑地存储,那是另一个奖励。但它不应包含有关它所标识的行中实体的任何信息,除了该实体的唯一标识符。

【讨论】:

  • @alexantd:很好的答案。我喜欢它足以接受它,但我有一个问题。假设创建 product_id 列是一种更可接受的方法;在这种情况下,该列也将是唯一的。给定这个属性,它不能作为主键吗?是的。此时,您的主键是一个独特的字段,在许多情况下也很有用。立即识别 ID 有什么问题?一看就知道它代表什么?尤其是如果维护它的成本是最小的(根据问题领域的现实来衡量,而不是理论)?
  • @gmale: 1) 区分主键和唯一键很重要。主键始终是唯一的,但反之亦然。您的产品还有其他独特的属性:名称、SKU 等。为什么不将其中一个作为主键呢? 2) 可能会立即识别主键 - 但风险是没有其他人会知道它代表什么。是产品ID吗? 2 周内的销售量?卡路里数?等等 3)如果你做对了,维护会更低。 :)
  • @alexantd:“区分主键和唯一键很重要”。为什么?主键仅表示表的任何键。主键与任何其他键没有根本不同。因此,出于实际原因,将一个键指定为“主”键通常有用,但这样做并不是特别重要,即它不会对键的形式或功能产生任何影响数据库。
  • @dportas: 1) 主键不能为空。唯一的密钥(本身)可以。 2) 确实可以设计一个没有主键的数据库,但 OP 并没有这样做。 (我认为“ID”的意思是“主键”......如果这是错误的,他/她可以纠正我 - 这当然会使所有这些答案完全无效:)
  • 没有键可以为空。这是所有键的要求,而不仅仅是您指定为“主要”键的键。如果您有一个可为空的列,那么这显然不是键。
【解决方案3】:

好吧,鉴于您的10141“Apple 在第四个过道”,当您最终将产品10 放在货架上1 上时会发生什么情况41?还是那个产品1 在货架上的014 货架上1,或者它是在货架上的产品101 41 坐在地板上,因为它不在货架上?

一旦您开始像这样混合数据,您通常会失去任何可靠地提取组件的能力。人类可读的密钥都很好而且很漂亮,但你永远不会破坏人类形式所基于的各个 ID。

【讨论】:

  • 是的,您已经缩小了这种方法的主要挑战之一。在我们的案例中,经过一个小时左右的思考后,我们确定了适合我们所有数据的约定,并有足够的扩展空间。在那种情况下,拥有可识别的ID真的有那么糟糕吗?无论如何,如果它们应该是任意的,那么本质上什么都不会丢失。唯一的“挑战”是维护——这个问题很容易通过电子表格和公式解决——更不用说也很容易识别不正确的 ID(此外,大多数都被外键约束捕获)。
【解决方案4】:

“可读”是什么意思? ID 通常只是数字。 “一致”是什么意思? ID 通常只是递增的数字;你不能得到比这更一致的了。当信息已经明确地存在于数据库中时,为什么还要浪费时间和精力尝试将信息编码到 ID 中?谁会使用“有序”的 ID?

【讨论】:

  • +1 表示干燥。 100% 同意。
【解决方案5】:

这是我对代理键的看法。 (或 ID,如果你想这样称呼他们)

代理键没有商业意义。它们用于唯一标识行。但他们所做的不仅仅是识别行。他们也是行的“灵魂”。它不能更改或交易。如果代理遵循“灵魂”原则,那么当您删除行时,新行将永远不会取死行的值。灵魂已经死去,仍然属于被删除的行。

我喜欢我的代理人是“灵魂”,尽管这不是代理人所必需的。

代理的优点是它永远不需要改变。如果其他 30 个表对您的主表有外键,则您不想在主表的 PK 更改时更新所有 30 个。您仍然可以在该可能更改的值上使用 CANDIDATE 键,但由于它可以更改,因此它不是行的灵魂。

代理键通常是自增整数。这非常适合聚集索引。您的表连接将尽可能好。自然键往往会产生可怕的聚集索引,因为新值很少是连续的。整数是小的、固定长度的数据类型,用于更快的匹配。

如果你的名字改变了,你还是你。如果你烧掉你的指纹,你还是你。上帝正在使用代理键,所以我认为我们可以在数据库中使用它们。

编辑 在更仔细地阅读了您的问题之后,我认为您实际上是以错误的方式使用“无意义的键”。

您有值“10141”来表示苹果/位置关联。那就是将 2 个代理项组合到 1 个字段中。将它们保留为单独的字段“101”和“41”,并在这些字段的组合上进行 PK。将它们分开将使搜索、索引、表连接等更容易。

您是对的,您不需要映射表上的另一个代理项。 2 个代理人的组合本身就是一个代理人(尽管不是灵魂)。只需在 2 个单独的列中表达组合,而不是合并为 1 列。 结束编辑

【讨论】:

  • @Tydus:我认为您找到了一个很好的解决方案:使用重要字段组合的 PK。在我们的例子中,这些字段通常是外键。所以这仍然归结为同一个问题:故意使这些外键“可识别”是否可以接受?
【解决方案6】:

有意义的 id 不违反“数据库设计策略”!

恰恰相反,这正是真正的关系数据库从一开始就是这样的。如果您的数据包含某些属性组合——从业务角度来看——是唯一的,那么不将其设为 ID 通常会破坏 Boyce-Codd 范式。并带来随之而来的异常。

除非 ID 中编码的信息与其他字段中的信息冗余,否则请使用它。如果是多余的,就做一个多列的主键。它们在使用 ORM 时不是很方便,但在数据驱动的应用程序中它们是一种祝福。

ADDENDUM:(原问题编辑后)

在您的情况下,对于数据驱动的应用程序,我会这样做:

Type
==========
Fruit
Veggie

Product
==========
Apple    Fruit
Banana   Fruit
Orange   Fruit
Lettuce  Veggie
Onion    Veggie
Carrot   Veggie

Isle
==========
4
5

Shelf
==========
top
bottom

Location
==========
4   top
4   bottom
5   top
5   bottom

ProductLocation
==========
Apple    4  top
Banana   4  top

有了这样的设置:

  • 数据已标准化
  • 您可以在 ProductLocation 表中查看任何产品的位置 - 您甚至可以查看货架
  • 没有代理
  • 根据查询的类型,这种结构实际上可以比其他命题执行得更好,因为它需要的连接更少(或者它可能更慢,因为它需要更多的存储空间)。
  • 这最适用于支持“替换更新”约束的 RDBMS。
  • 如果您想将名称视为 id,您可能需要添加一些列,例如“显示名称”。这是因为人们想要更改显示的内容比他们想要更改的身份更频繁。

【讨论】:

  • +1 我喜欢你在这里所说的:根本不要使用数字 ID。采用这种方法可能会解决我们数据的所有问题。但是......如果要求必须使用数字 ID(我确信这会发生),那么让这些 ID “可识别”而不是任意/自动递增是非常错误的吗?尤其是当,1) 我们的 ID 永远不会改变 2) 数据保存在电子表格中,其中包含许多公式,这使得保持一致非常容易。
  • @gmale:问题来自重复数据。 ID 不应包含任何其他列中可用的数据或通过加入不同的键可用的数据。复制的列是否是主键或是否以直接或复杂的方式复制都无关紧要。
【解决方案7】:

关键设计的三个主要标准是熟悉度、简单性和稳定性。如果您使用熟悉且简单的键,那么用户会发现它们更容易识别、记忆和使用。他们在输入和使用键值时出错的可能性较小,并且通常会提高数据质量和可用性。

我建议您通过询问您的用户他们认为哪种类型的标识符更易于使用来解决这个问题,或者如果这对您非常重要,甚至可以与他们一起测试不同的方案。无论如何,单靠开发人员不应该做出这样的决定。一些组织有部门或个人负责定义要使用的标准编码方案。

【讨论】:

  • 此答案侧重于谁使用数据。我喜欢这样——实际上,这是最重要的事情。这有助于我看到这是问题的根源。在我们的例子中,整个问题的出现是因为唯一能看到或关心 ID 的人是测试/维护应用程序的人。无一例外,这些人发现,当他们能够轻松理解 ID 时,他们可以更快地完成工作,而无需交叉引用表。问题是,使这些 ID “一致”和“可读”显然被认为是最纯粹的邪恶形式。
【解决方案8】:

ID 可能对有意义,但不一定对计算机有意义。数据库软件不会足够智能以识别这样的模式(除非您对其进行编程,显然,这将比它的价值更麻烦)所以实际上您所做的只是创造一个潜在的问题当与您未预见到的 ID 发生冲突时,为您自己解决问题。

我理解您的意思,但良好的数据库设计涉及使数据库引擎的读写操作尽可能简单。您最好设置索引并研究数据库性能以找到可以优化的领域。

【讨论】:

  • 那么,当你写一个程序时,你的首要任务是让COMPUTER易于阅读?显然,您使用某种汇编程序?
  • @fdreger:我的首要任务是让我的源代码易于阅读,并使我的数据易于我的计算机处理。跨度>
  • @Oli:如果您的数据您的源代码会怎样。一般来说,Spring 和依赖注入的权衡之一是它将大量代码移动到 XML 文件和/或数据库中。无论哪种方式,它都会将过去在代码中有意义的东西转移到数据中。那时,作为开发人员,您将需要以前所未有的方式理解您的数据。我开始理解更多反对我所概述的方法的论点,但其中大部分似乎都沉浸在传统中,而不是可证明的论点。 Java 的 Spring 无疑让我重新评估了我如何使用数据。
  • @fdreger 我们在谈论源代码吗?奇怪,我记得这是关于数据库优化的讨论,在这种情况下,您应该尽一切努力使计算机更容易阅读。
  • @Jarrod:你记错了,这与优化无关(=使数据处理速度更快)。最初的问题是关于一致性、可维护性和可读性。 “尽一切努力让计算机更容易阅读”会降低这三种品质。例如,使用 OOP 或脚本语言会使计算机更难,而对人来说更容易。
【解决方案9】:

Zooko's Trianglepetnames 的概念可能与此相关。

【讨论】:

  • 谢谢。这些都是有趣的读物。
【解决方案10】:

我考虑过发表评论,但我担心它可能过于牵涉。

我认为共识意见是,一般来说,ID 不应该有意义。也许如果您将问题更多地限制在您的场景的具体细节上,那么意见会有所不同?

根据您的 cmets,听起来您正在从电子表格中加载数据,我假设您正在使用有意义的 ID 作为确定不同数据之间关系的一种方式?

您不能让数据库处理自增 ID,而是让用户(开发人员?)定义代码,这是有原因的吗?通过这种方式,您可以通过外键保持引用完整性,并且可以正确规范化。如果你真的需要快速浏览一下 data 那么你可以有一个具有某种命名约定的计算列。它可能对您的需求更有意义?

例如

Code Description
==== ===========
F    Fruit
V    Veggie

Product Code Product Type Product Description
============ ============ ===================
AP           F            Apple
BA           F            Banana

Location Code Location Description
============= ====================
AFTS          Aisle four top shelf
AFBS          Aisle four bottom shelf


Product Code Location 
============ ========
AP           AFTS 
BA           AFTS

实际上位置可以进一步规范化为过道和货架,但你明白了。

当数据插入数据库时​​,会为每条记录创建 ID,并且可以通过代码确定关系,并且可以将外键设置为正确的 ID。然后,您的应用程序可以在不了解代码的情况下处理 ID。

所以产品位置看起来像:

Product ID Location ID
========== ===========
1          1 
2          1

如果您仍然需要更具描述性的内容,您可以在 SQL 中进行连接以获取代码或创建计算列,或者您的应用可以将 ID 映射到缓存中的代码。

例如

Product ID Location ID ProductCode_LocationCode
========== =========== ========================
1          1           AP_AFTS
2          1           BA_AFTS

这会对性能造成一点影响,我仍然不明白这一点,但也许这对你有帮助?

好吧,那太长了。 :)

【讨论】:

  • 感谢您抽出宝贵时间创建此回复。它很好地说明了你的观点。这样做的主要问题是它要求计算列在 ID 所在的所有位置都可用,否则将需要交叉引用。问题域的实际情况是,在许多情况下,您(开发人员/调试人员)被困在查看一个 ID(并且只有一个 ID)并且想知道“这到底代表什么?”某些 ID 在学习后自然会开始具有意义。如果很容易实现,那么促进这种识别有什么坏处?
  • @gmale:我不确定我是否曾经陷入过只有身份证的情况。
  • @gmale:我想我知道你所说的交叉引用的烦恼是什么意思。很难读取包含 foo1_id、foo2_id、foo3_id 等列的行,所有这些列都包含在查找之前没有意义的任意数字。 1) 即使使用定制的 ID,您仍然只能获得有关该对象的一部分数据,并且您仍然需要查找它,除非您已经记住它。 2) 视图在这些情况下非常有用。
【解决方案11】:

我不认为这有什么不同。当我有机会时,我总是倾向于重新设定我的身份,但这只是我。我想如果你在代码中引用它们[例如枚举],那么在你的 id 中有一些顺序会很有用,除此之外我不会担心。

【讨论】:

  • 我同意。我曾使用过以这种方式“设计”的系统。与他们一起工作很痛苦,因此保持 id 较低似乎更易于管理。
猜你喜欢
  • 1970-01-01
  • 2015-09-21
  • 1970-01-01
  • 1970-01-01
  • 2012-01-27
  • 2011-05-14
  • 2010-09-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多