为什么要在SQL文本列中存储分隔列表?

时间:2009-02-10 16:02:02

标签: database-design

我必须维护一个具有大量文本数据类型的列的应用程序,其中插入了多个值,用逗号,斜杠或有时甚至管道(|)字符分隔。我想弄明白你为什么要这么做。

例如,订单表有一个名为详细信息的列,其中包含以下信息:

2x #ABC-12345  Widget, Black: $24.99 /4x #ABC-12344 Widget, Blue: $23.50

/分隔订单项的位置;有一个VBScript代码,它从记录集中读取值并在For循环中解析它以显示使用类似的东西(这几乎就是代码读取的方式,变量名称和所有内容)arydtls = split(rstmp("details"), "/") 。在各种表的代码中重复此方法。

在我看来,将它们放在一个单独的表格中并将其链接回来就更好100倍(更不用说更容易合作了)(有趣的是,对于Orders它确实执行此操作,但数据并不总是与详细信息文本字段匹配,因为OrderDetail表已在代码中更新;详细信息字段在应用程序中被视为只读。

我的前任是否知道我没有,或者我说“WTF?!!”当我看这个架构?它看起来像是非常低效且难以维护,并且它使运行报告更加困难,因为我需要的数据可能包含在文本字段中,或者它可能位于十几个具有相似信息的表中并且用于不同的表中部分申请。

11 个答案:

答案 0 :(得分:8)

两种最有可能的情况是:

  • 您的前任不称职/不理解正常化
  • 您的前任在规范化结构中遇到了一些性能问题,并发现此方法是一种改进

由于规范化在查询操作方面通常非常昂贵,因此我们有时可以通过消除昂贵的连接并在应用程序端针对单行进行操作来获得性能提升。

数据库设计没有绝对的规则,即“在一个行中存储分隔值对于这种情况更好”。这些都是针对您的特定数据集和您的使用模式进行测试,并在必要时进行改进。

根据我的经验,虽然这种模式比标准化有所改善并不常见......但这非常不典型。

编辑:第三种可能性是每行具有n值是对原始模式的更改,而不是添加新表,而是您的前任调整了列的大小。这并不一定与“无能”选项有所不同:)但有时在数据库模式更改中涉及政治压力......

答案 1 :(得分:2)

  

我的前任是否知道我没有,或者我说“WTF?!!”当我看这个架构?

不,你的前任没有。是的,你是对的。但是,请参见最后的注释。

  

看起来像这样效率极低且难以维护,并且它使运行报告更加困难,因为我需要的数据可能包含在文本字段中,或者它可能位于十几个具有类似信息的表中,用于应用程序的不同部分。

非常低效。但是,请参见最后的注释。

列应该始终是行的不可分割的属性。我在本专栏中看到了三个(可能是四个)属性的两个副本:

2x #ABC-12345  Widget, Black: $24.99 /4x #ABC-12344 Widget, Blue: $23.50
  • quanity(2x / 4x)。
  • 代码(#ABC-12345 /#ABC-12344)。
  • description(Widget,Black:/ Widget,Blue :) [可能是描述颜色属性]。
  • 价格($ 24.99 / $ 23.50)。

这可以更好地设计为:

StockItems
    Code char(10) primary key
    Desc varchar(50)
Transaction
    TxnId something primary key
    : : :
TransactionPart
    TxnId something \
    TxnSeq int      / primary key
    Quantity integer
    Code char(10) foreign key StockItems(Code)
    Price float

注意:

这可能是为了在面对数据库中其他地方不断变化的值时保留历史信息。例如,如果库存项目的描述发生变化或项目被删除。

但是,不是处理它的正确方法。在这种情况下,外键约束将阻止项目代码被删除,并且应该有进程以防止更新描述(例如对库存项目代码进行版本控制)。

当然,如果你从不要搜索该列中的任何项目,这是完全有效的,但在未来可能搜索它们的功能方面是不明智的。

也许在此表中搜索过的唯一内容是客户代码 - 然后一个自由格式的文本字段就足够了。

仍然不会那样做,但是如果需要添加搜索功能,可以在以后更改数据库模式的YAGNI参数

答案 2 :(得分:1)

很简单,他要么有理由,要么没有,没有要求不可能知道。如果你假设他不是一个完全的想法和一些可能的原因,那么可能是下面的一个。

如果数据仅供参考,并且“永远不会改变”,因为您经常听到,那么将显示字符串直接投射到该字段可能是一个快速的胜利。毕竟,只需用Tabs替换管道和BR的斜线就可以将它放在屏幕上非常容易。如果代码 写得非常快,那么这可能是最简单的选择。

自SQL 2005以来的一项新功能是XML数据类型。这方面的一个主要用途是您可以针对特定记录存储和索引未知数量的值。你可能会关心一件事的颜色,另一件事的尺寸,其他东西的重量。您可能无法生成这些事物的确切列表,并且存储此数据的真正规范化的通用方法可能对于系统来说太慢或过于复杂。这可能是试图获得类似功能的一种方法。

关键在于,大多数事情都是有原因的。你试图找出这个原因,你已经看到了正确的方法。有一天你可能会碰到它并想“哦,是的!”。只是从你自己的角度看待某些事情往往会导致人们无法看到树木的情景。

答案 3 :(得分:0)

WTF真的。切勿将此类内容存储在数据库中。

答案 4 :(得分:0)

你的前任可能还有其他一些想法,但这还未完成?

我可以告诉你,这对性能来说非常糟糕

您如何创建一个将返回谁购买蓝色小部件的查询?你将不得不扫描整个表,然后解析该信息,如果有另一个表,这是规范化的,那么这将是更好的性能明智

答案 5 :(得分:0)

我在某个企业软件中看到过一个数据库,这个数据库在很多地方都有。从维护角度和性能角度来看,这都非常糟糕。引用的原因通常是:

  • 它“更简单”因为它不需要连接
  • 它更快,因为它不需要连接
  • 它不会使包含大量表格的数据库混乱

现在,第一点可能是正确的,但它只是“更简单”,直到你想要查询它。现在你搞砸了。所以我会说这有效地被驳斥了。第二点也是如此,只要你不反对它。一旦你必须读入整个表格,解析数据,然后对你的应用程序中的行进行过滤,就会丢失。最后一个总是如此,但谁在乎数据库是否“混乱”?这就是它的用途!体面的RDBMS将允许您将表格放入多个模式中,这有点像命名空间,有助于打击混乱。一个好的命名约定也有帮助(但如果你使用匈牙利疣,那么帮助你神仙)。

简而言之,这是一个坏主意。我希望你能够解决这个问题,但很可能你只需按照原来的条款处理它......

答案 6 :(得分:0)

在Universe等操作系统中,UniData数据存储在由

等分隔的文件中

Char(254)=分隔属性 Char(253)=分隔属性中的多个值 Char(252)=分离子子值 等等

令人震惊的不是它:-)每当我和仍然使用DataBasic的前同事交谈时,他们会问我使用的第一个问题是他们问的第一个问题是“它能处理多个值吗?”

在RDBMS中,我们有一个Order表和一个OrderLine表。 OrderLine上的PK很可能类似于OrderNumber,LineNumber。

在UniData等中,他们要做的是在Order中有一个名为“Lines”的属性,它将保存OrderLine文件的键列表,复合键通常用星号分隔。

  • 1234 * 1
  • 1234 * 2
  • 1234 * 3

然后,当他们从文件中将订单加载到内存中时,他们有一个密钥列表,他们需要从OrderLine文件加载OrderLines。请注意,这些是文件而不是表: - )

在我看来,喜欢这种旧式存储数据方式的人试图使用关系数据库,根本不理解它,然后尝试使其像UniData一样工作。

麻袋他们: - )

答案 7 :(得分:0)

我不能说你的前任在想什么。正如Rex M所说,有时政治压力会导致奇怪的实施。

很多人把一个项目列表填入表中的单个值,试图绕过(旧式)第一范式的限制。缺点是查询必须在应用程序中以编程方式完成,而不是在WHERE子句中使用简单的citerion。

大约10年前,Oracle增加了将表放入值的功能。大约在同一时间,Date重新定义了1NF,以便所有关系自动在1NF。这包括包含其他关系的rleations。如果没有该功能,最简单和最强大的设计是将重复的项目分解为单独的值,每个项目都有一行。

(例如:学生注册的课程列表)

在很多情况下,根本原因是设计师无知或顽固。同样,我不知道你的前任面临的限制。除非你必须,否则不要模仿他。

答案 8 :(得分:0)

你为什么要这样做?

几十年前,我的妻子回过头来捣乱,我的妻子选择了Pick系统,其中包括一个数据库和一个BASIC等。 Pick数据库和语言在将数组放入数据库字段时效果很好(不确定我是否应该将它们称为列)。所以,有一个完全合理的环境。

我不知道Pick是否还在,但我很久没有听说过了。这个表可能是一个Pick数据库(很糟糕地)被翻译成一个基于SQL的数据库,而且编写它的人可能是一个前Pick开发人员,当时还没有学会如何使用关系数据库。

上次我遇到这样的数据库时,我问道。原来它是由前Pick开发人员设计的。

我不会称这个设计能胜任,除非这真的是作为一个只写的可忽略的领域,但很可能是设计师并不愚蠢。

答案 9 :(得分:0)

一种可能的,某种可能的正当理由,可能是数据结构不固定,详细属性与订单实例非常不同。

在数据库强加的静态结构中使用动态属性并不容易。例如,XML结构更适合这种情况,但是给出xml固有的冗长,'csv like'方法可能是一种更有吸引力的选择。

答案 10 :(得分:0)

对我来说看起来像个WTF。它与其他表的实现方式不一致,而且效率肯定不高。当你在不知道内部数据的情况下查看模式时,很容易误解列的意义。

然而,可能有一个原因,为什么过去的开发人员已经这样做了,你能给我们更多的信息,比如关于业务逻辑吗?感谢