16

2009.04.24 更新

我的问题的重点不是开发人员的困惑以及如何处理它。

关键是要了解分隔值何时是正确的解决方案。

我见过商业产品数据库中使用的分隔数据(Ektron 哈哈)。

SQL Server 甚至具有 XML 数据类型,因此可以用于与分隔字段相同的目的。

/结束更新

我正在设计的应用程序有一些多对多的关系。过去,我经常使用关联表在数据库中表示这些。这给开发者带来了一些困惑。

这是一个示例数据库结构:

Document
---------------
ID (PK)
Title
CategoryIDs (varchar(4000))


Category
------------
ID (PK)
Title

Document 和 Category 之间存在多对多的关系。

在此实现中,Document.CategoryIDs 是一个大的以竖线分隔的 CategoryID 列表。

对我来说,这很糟糕,因为它需要在查询中使用子字符串匹配——这不能使用索引。我认为这会很慢并且不会扩展。

使用该模型,要获取某个类别的所有文档,您需要以下内容:

select * from documents where categoryids like '%|' + @targetCategoryId + '|%'

我的解决方案是创建一个关联表,如下所示:

Document_Category
-------------------------------
DocumentID (PK)
CategoryID (PK)

这让开发人员感到困惑。我缺少一些优雅的替代解决方案吗?

我假设 Document 中会有数千行。类别可能像 40 行左右。主要关注的是查询性能。我是否过度设计了这个?

是否存在优先将 ID 列表存储在数据库列中而不是将数据推送到关联表的情况?

还要考虑我们可能需要在文档之间创建多对多关系。这将建议一个关联表 Document_Document。这是首选设计还是将关联的文档 ID 存储在单个列中更好?

谢谢。

4

9 回答 9

33

这让开发人员感到困惑。

获得更好的开发人员。这是正确的做法。

于 2009-04-24T17:53:56.503 回答
26

您的建议是优雅、强大、最佳实践的解决方案。

由于我认为其他答案不够强烈,因此我将这样做。

如果您的开发人员 1) 无法理解如何在关系数据库中建模多对多关系,并且 2) 强烈坚持将您的 CategoryID 存储为分隔字符数据,

然后他们应该立即失去所有数据库设计权限。至少,他们需要一个真正有经验的专业人士加入他们的团队,有权阻止他们做这种不明智的事情,并且可以为他们提供他们完全缺乏的数据库设计培训。

最后,您不应该再次将他们称为“数据库开发人员”,直到他们适当地跟上速度,因为这对我们这些真正有能力的开发人员和设计人员来说是轻微的。

我希望这个答案对你很有帮助。

更新

我的问题的重点不是开发人员的困惑以及如何处理它。

关键是要了解分隔值何时是正确的解决方案。

除极少数情况外,分隔值是错误的解决方案。当单个值将被查询/插入/删除/更新时,这证明这是错误的决定,因为您必须解析并触摸所有其他值才能使用所需的值。通过这样做,您违反了第一个(!!!)正常形式(这句话对您来说应该听起来像一个令人难以置信的卑鄙的咒骂)。使用 XML 做同样的事情也是错误的。在列中存储分隔值或多值 XML可能是有意义的,因为它被视为不可分割且不透明的“属性包”,数据库不查询但始终将其全部发送给另一个消费者(可能是 Web 服务器或EDI 收件人)。

这让我回到我最初的评论。在我的书中,那些认为违反第一范式是个好主意的开发人员是非常缺乏经验的开发人员。

我承认有一些非常复杂的非关系数据存储实现,使用文本属性包(例如 Facebook(?)和其他在数千台服务器上运行的数百万用户站点)。好吧,当您的数据库、用户群和每秒事务量大到足以需要它时,您将有足够的钱来开发它。同时,坚持最佳实践。

于 2009-04-24T18:09:19.383 回答
17

使用逗号分隔的 ID 几乎总是一个大错误。
RDBMS 旨在存储关系。

于 2009-04-24T17:55:39.087 回答
16

我的解决方案是创建一个关联表,如下所示:这让开发人员感到困惑

真的吗?这是数据库 101,如果这让他们感到困惑,那么他们可能需要远离他们的向导生成的代码并学习一些基本的数据库规范化。

你提出的是正确的解决方案!

于 2009-04-24T17:56:10.677 回答
11

您设计中的 Document_Category 表无疑是解决问题的正确方法。如果可能的话,我建议您教育开发人员,而不是提出次优的解决方案(并且会影响性​​能,并且没有参照完整性)。

您的其他选项可能取决于您使用的数据库。例如,在 SQL Server 中,您可以拥有一个 XML 列,该列允许您将数组存储在预定义的架构中,然后根据该字段的内容进行连接。其他数据库系统可能有类似的东西。

于 2009-04-24T17:57:09.647 回答
6

您正在执行的多对多映射很好且已标准化。如果需要,它还允许稍后添加其他数据。例如,假设您想添加将类别添加到文档的时间。

我建议在 document_category 表上也有一个代理主键。如果这样做有意义的话,还有一个 Unique(documentid, categoryid) 约束。

为什么开发人员会感到困惑?

于 2009-04-24T17:55:39.510 回答
6

“这让开发人员感到困惑”的设计意味着您的开发人员教育不足。这是更好的关系数据库设计 - 如果可能,您应该使用它。

如果您真的想使用列表结构,请使用能够理解它们的 DBMS。此类数据库的示例是U2 (Unidata, Universe) DBMS,它是(或曾经是很久以前)基于 Pick DBMS。可能还有其他类似的 DBMS 提供程序。

于 2009-04-24T18:14:58.137 回答
5

这是经典的对象关系映射问题。开发人员可能并不愚蠢,只是缺乏经验或不习惯以正确的方式做事。大喊“3NF!” 一遍又一遍不会说服他们正确的方法。

我建议您请您的开发人员向您解释他们将如何使用管道分隔的方法按类别获取文档计数。这将是一场噩梦,而链接表使它变得非常简单。

于 2009-04-24T18:34:06.717 回答
5

我的开发人员尝试这种“数据库列中的逗号分隔值”方法的第一个原因是,他们认为添加一个新表来满足对多个值的需求将花费太长时间来添加到数据模型和数据库。

他们中的大多数人都知道由于各种原因他们的工作很糟糕,但他们选择了这种次优方法,因为他们只是可以。他们可以做到这一点并且可能永远不会被抓住,或者他们会在项目的后期被抓住,因为修复它的成本太高且风险太大。他们为什么这样做呢?因为它们的性能仅根据速度而不是质量或合规性来衡量。

也可能是,就像在我的一个项目中一样,开发人员一个表来放置多个值,但他们认为在父表中复制该数据会提高性能。他们错了,他们被叫出来了。

因此,虽然您确实需要解决如何处理这些代价高昂、风险大且损害商业信心的技巧,但您还应该尝试找出开发人员认为采取这种行动方案在短期和长期内更好的原因为项目和公司。然后修复感知和数据结构。

是的,这可能只是懒惰、恶意或无知,但我敢打赌,大多数时候开发人员都会做这些事情,因为他们经常被告知“完成它”。我们在数据模型和数据库设计方面需要确保我们不会发送错误信息,说明我们对满足新实体/表/信息的业务需求的请求的响应速度。

我们还应该看到,数据人员需要不断监控我们数据架构的“竣工”部分。

就个人而言,我从不授权在关系数据库中使用逗号分隔值,因为实际上构建新表比构建解析例程来创建、更新和管理列中的多个值处理所有引入异常是因为有时该数据也嵌入了逗号。

最重要的是,不要使用逗号分隔值,而是找出开发人员想要这样做的原因并解决该问题。

于 2009-05-09T19:52:20.433 回答