使用多个由逗号分隔的外键是否错误,如果是,为什么?

Sar*_*nyu 32 foreign-key database-design

有两个表:DealDealCategories。一笔交易可以有多个交易类别。

所以正确的方法应该是制作一个DealCategories具有以下结构的表:

DealCategoryId (PK)
DealId (FK)
DealCategoryId (FK)
Run Code Online (Sandbox Code Playgroud)

但是,我们的外包团队以Deal这种方式将多个类别存储在表中:

DealId (PK)
DealCategory -- In here they store multiple deal ids separated by commas like this: 18,25,32.
Run Code Online (Sandbox Code Playgroud)

我觉得他们这样做是错误的,但我不知道如何清楚地解释为什么这是不对的。

我该如何向他们解释这是错误的?或者也许是错了,这是可以接受的?

Sim*_*rts 49

是的,这是一个可怕的想法。

而不是去:

SELECT Deal.Name, DealCategory.Name
FROM Deal
  INNER JOIN
     DealCategories ON Deal.DealID = DealCategories.DealID
  INNER JOIN
     DealCategory ON DealCategories.DealCategoryID = DealCategory.DealCategoryID
WHERE Deal.DealID = 1234
Run Code Online (Sandbox Code Playgroud)

你现在必须去:

SELECT Deal.ID, Deal.Name, DealCategories
FROM Deal
WHERE Deal.DealID = 1234
Run Code Online (Sandbox Code Playgroud)

然后你需要在你的应用程序代码中做一些事情来将该逗号列表拆分为单独的数字,然后单独查询数据库:

SELECT DealCategory.Name
FROM DealCategory
WHERE DealCategory.DealCategoryID IN (<<that list from before>>)
Run Code Online (Sandbox Code Playgroud)

这种设计反模式源于对关系建模的完全误解(您不必害怕表格。表格是您的朋友。使用它们),或者一种奇怪的误导性信念,即采用逗号分隔的列表并将其拆分会更快在应用程序代码中添加链接表(从来没有)。第三种选择是他们对 SQL 没有足够的信心/能力来设置外键,但如果是这种情况,他们不应该与关系模型的设计有任何关系。

SQL Antipatterns (Karwin, 2010) 用一整章来介绍这个反模式(他称之为“Jaywalking”),第 15-23 页。此外,作者在 SO上发布了一个类似的问题。他指出的关键点(适用于这个例子)是:

  • 查询特定类别中的所有交易相当复杂(解决该问题的最简单方法是正则表达式,但正则表达式本身就是一个问题)。
  • 如果没有外键关系,您就无法强制执行参照完整性。如果您删除 DealCategory nr。#26,然后,在您的应用程序代码中,您必须遍历每笔交易以查找对类别 #26 的引用并删除它们。这是应该在数据层处理的事情,而不得不在您的应用程序中处理它是一件非常糟糕的事情
  • 聚合查询(COUNTSUM等),同样,从“复杂”到“几乎是不可能的”变化。询问您的开发人员他们如何为您提供所有类别的列表以及该类别中的交易数量。如果设计得当,那就是四行 SQL。
  • 更新变得更加困难(即,您的交易分为五个类别,但您想删除两个类别并添加另外三个类别)。这是具有适当设计的三行 SQL。
  • 最终你会遇到VARCHAR列表长度限制。尽管如果您有一个超过 4000 个字符的逗号分隔列表,那么解析该怪物的速度可能会很慢。
  • 从数据库中拉出一个列表,将其拆分,然后返回数据库进行另一个查询本质上比一个查询慢。

TLDR:这是一个从根本上有缺陷的设计,它不会很好地扩展,即使是最简单的查询也会增加额外的复杂性,而且开箱即用它会降低您的应用程序的速度。

  • 我不完全确定他们是否想要在 Deals 和 Categories 之间建立多对多的关系,或者某种类别的层次结构。无论哪种方式,它都是主要观点的副业,用逗号分隔的字段而不是链接表是一个坏主意。 (2认同)