数据库设计 – 使用逗号分隔的多个外键是错误的,如果是,为什么?

有两个表:Deal和DealCategories.一笔交易可以有很多交易类别.

所以正确的方法应该是使用以下结构制作一个名为DealCategories的表:

DealCategoryId (PK)
DealId (FK)
DealCategoryId (FK)

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

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

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

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

解决方法

是的,这是一个糟糕的主意.

而不是去:

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

你现在必须去:

SELECT Deal.ID,Deal.Name,DealCategories
FROM Deal
WHERE Deal.DealID = 1234

然后,您需要在应用程序代码中执行操作,将该逗号列表拆分为单个数字,然后单独查询数据库

SELECT DealCategory.Name
FROM DealCategory
WHERE DealCategory.DealCategoryID IN (<<that list from before>>)

这种设计反模式源于对关系建模的完全误解(你不必害怕表格.表格是你的朋友.使用它们),或者一种奇怪的错误信念,以逗号分隔的列表并拆分它会更快在应用程序代码中,而不是添加链接表(它永远不会).第三种选择是他们对sql没有足够的信心/能力来设置外键,但如果是这样的话,他们就不应该与关系模型的设计有任何关系.

SQL Antipatterns(Karwin,2010)将整整一章用于这个反模式(他称之为’Jaywalking’),第15-23页.此外,作者已在similar question over at SO发布.他指出的关键点(适用于此示例)是:

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

TLDR:这是一个根本上有缺陷的设计,它不能很好地扩展,它甚至可以为最简单的查询带来额外的复杂性,并且开箱即用会降低您的应用程序速度.

相关文章

SELECT a.*,b.dp_name,c.pa_name,fm_name=(CASE WHEN a.fm_n...
if not exists(select name from syscolumns where name=&am...
select a.*,pano=a.pa_no,b.pa_name,f.dp_name,e.fw_state_n...
要在 SQL Server 2019 中设置定时自动重启,可以使用 Window...
您收到的错误消息表明数据库 &#39;EastRiver&#39; 的...
首先我需要查询出需要使用SQL Server Profiler跟踪的数据库标...