【问题标题】:Database Design: Categories in their own table?数据库设计:自己的表中的类别?
【发布时间】:2011-10-02 05:17:42
【问题描述】:

我正在将一些数据库重新设计成一个包含数据库的数据库,并且我注意到旧数据库的先前设计者喜欢将类别存储在自己的表中。比如有一个tableboats(bid: integer, bname: string, color: integer),在应用中有一个下拉框可以让用户指定小船的颜色,那么就有一个表颜色(cid:整数,cname:字符串)。我不会包括颜色表,只是将颜色作为字符串放在船表中。我意识到这减少了颜色名称的冗余存储,但是将船表与颜色表连接起来所增加的运行时成本“值得”吗?此外,下拉列表中填充了 SELECT cname FROM color 语句,而我会在 SELECT DISTINCT color FROM boats 上定义一个视图来填充下拉列表。

这个例子很简单,但是在我重新设计的系统中这种情况发生了多次,即使对于只有两个选项的类别也是如此。这导致许多表只有 2 个字段。有些只有 1 个字段(我还没有弄清楚它们的用途,但我认为它们只是用来填充下拉列表,实际的表格也包含这些值)。

【问题讨论】:

    标签: database database-design


    【解决方案1】:

    如果这是我的数据库,我会亲自将它们保存在自己的表中。

    如果您遇到Boats a,b and c can only come in silver and black 的要求,那么您会很感激您这样做了。我已经在很多项目中看到了这些类型的请求。

    如果您只关心查询的复杂性,您可以创建一个视图来连接您需要的信息,这样您只需要查询一次并且不需要JOIN

    如果您担心 JOIN 对性能的影响,那么我会考虑创建适当的索引或可能的索引视图。

    祝你好运!

    【讨论】:

    • 查看或存储过程取决于她使用的数据库和所需的索引功能
    • 感谢您的建议,它们非常有帮助。这是我第一次在校外/我自己的娱乐之外开发数据库,​​而且我几乎是一个人工作。
    【解决方案2】:

    当您知道一列应该有一组有限的值时,您应该告诉 dbms 强制执行该有限集。处理这种需求的三种最常见的方法是

    1. 忽略它,
    2. 设置对颜色表的外键引用,并
    3. 对颜色列表使用 CHECK() 约束。

    在这三个中,为颜色表设置外键往往会使生活变得更轻松。

    我意识到这会减少颜色名称的冗余存储,但是 是将船表与颜色连接起来的额外运行时间成本 表“值得”吗?

    这是一个不同的问题。首先,存储外键值是一种数据完整性形式,而不是一种冗余形式。密钥的存在有两个原因:1)识别现实世界中的事物,2)存储在其他表中。 (因为键标识的东西与另一个表相关。)

    其次,如果您通过为颜色分配任意 ID 号来识别颜色,您必须使用 JOIN 来获取人类可读的信息。但是颜色,就像许多属性一样,带有它们的身份。如果您使用颜色名称本身(“red”、“orange”等)或使用人类可读的名称代码(“R”、“O”等),则不需要连接。您确实仍然需要一个颜色表(或一个 CHECK() 约束),因为boats 中的列具有一组有限的值,并且 dbms 应该强制使用这组有限的值。

    所以你可以做这样的事情。

    create table boats (
      boat_id integer primary key,
      registered_name varchar(35) not null,
      hull_color varchar(10) not null references hull_colors (color)
    );
    
    create table hull_colors (
      color varchar(10) primary key
    );
    
    insert into hull_colors values ('red'),('orange'),('yellow') etc.
    

    这两个表都在 5NF 中。

    【讨论】:

    • 请确保我用我自己的话理解你:hull_colors 表应该包含所有可能的颜色,boats 表还存储每艘船的颜色,而不是 ID 号;但是为了限制boat.hull_color 的可能值,我们将该字段设置为引用 hull_colors 表的外键。这是一个很好的建议 - 感谢您的帮助。
    • “hull_colors 表应该包含所有可能的颜色”。船体的所有可能颜色。汽车的颜色完全不同,并且因制造商而异。对于单个制造商而言,“有效”的车身颜色每年、型号和型号、装饰套件都不同。
    【解决方案3】:

    通常最好有一个规范化的数据库。

    但是,在您的示例中,您可以使用Categories(ID, Type, Name) 表并将颜色存储为( 3, "Color", "Blue" ), ( 4, "Color", "Red" ), ... 这样,您可以在同一个表中存储更多类别,同时将它们分开存储。填充下拉列表需要简单地选择 select ID, Name from Categories where Type = 'Color' 形式。

    编辑:请注意,正如@Catcall 所说,此解决方案违反了数据库规范化的第一条规则。 3NF 表将是Colors(ID, Name)。这样,您可以使用其ID 来引用某种颜色。

    使用select distinct color from boats 填充下拉列表有很多缺点,例如,如果Boats 表不包含记录怎么办。然后,您的选择将不返回任何内容,并且下拉控件不会填充任何值。另一个问题是当您有包含'Red''red' 或类似的字段时。查看Database Normalization here的更多详情

    【讨论】:

    • "这样,你可以在同一张表中存储更多的类别,同时,将它们分开存储。"对我来说,这看起来像the EAV anti-pattern。 (EAV 从幻灯片 18 开始。)如果您想就该主题与 Bill Karwin 进行辩论,他是 SO 的成员。不过,研究归一化是一个很好的建议。对于我们所有人。
    • 感谢您的观察。我已经编辑了我的答案,以更接近我的意思。无论如何,为您的评论 +1。
    【解决方案4】:

    听起来这些是查找表,因此如果最终用户想要添加额外的颜色,那么他们可以将其添加到数据库中,然后它将在 UI 中传播。这也进入了规范化。如果只有一个地方引用了颜色,那么查找表就不是必需的。但是,如果有多个表为不同的事物引用颜色,那么查找表将为您省去很多麻烦。

    【讨论】:

      猜你喜欢
      • 2015-10-15
      • 2011-07-20
      • 2017-11-07
      • 2011-02-17
      • 1970-01-01
      • 2013-12-17
      • 2023-03-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多