只有1或2个可选字段的数据库表...分成多个表?

时间:2009-08-17 22:00:31

标签: sql database-design

在我正在设计的数据库中,有一个相当中心的表格,代表已出售或待售的东西。它区分个人销售(如eBay)和适当公司的销售。这意味着实际上有1个或两个字段不同于两种情况......例如,一个字段仅在一种情况下使用,另一个字段在一种情况下是可选的但在另一种情况下是强制性的。 如果有更多的专业知识,那么拥有核心表格然后两个表格与特定案例相关的字段是明智的。但是在这里,创建两个表只是为了包含一个字段加上对核心表的引用,这对于查询设计人员和数据库软件来说既美观又糟糕。

你怎么看?通过使用具有弱约束的单个表来稍微弯曲规则是否可以 - 这意味着DB不能100%防止添加不一致的数据(以非常有限的方式) - 或者我是否将其吸收并创建看起来愚蠢的1字段表?

6 个答案:

答案 0 :(得分:9)

对于公共列和特定于子类型的列的从属表,您使用一个表描述的内容称为Class Table Inheritance。这是一件非常好的事情。

@Scott Ferguson似乎在描述(这两种销售类型的两个不同的表)被称为Concrete Table Inheritance。它也可以是一个很好的解决方案,具体取决于您的需求,但更常见的是,它使得跨两个子类型编写查询变得更加困难。

如果您只需要一个或两个仅适用于给定子类型的列,我同意创建依赖表似乎有点过分。请记住,大多数品牌的SQL数据库都支持CHECK约束或触发器,因此您可以将数据完整性规则设计到元数据中。

CREATE TABLE Sales (
 sale_id SERIAL,
 is_business INT NOT NULL, -- 1 for corporate, 0 for personal
 sku VARCHAR(20),          -- only for corporate
 paypal_id VARCHAR(20),    -- mandatory but only for personal
 CONSTRAINT CHECK (is_business = 0 AND paypal_id IS NOT NULL)
);

答案 1 :(得分:3)

我认为拥有这些领域的选择今天不会伤害到你,而且我会选择这样做。请记住,随着数据库的发展,您可能需要决定重构为2个单独的表,(如果您需要更多字段)

答案 2 :(得分:1)

有些人坚持认为永远不允许使用不适用的字段,但我认为这是某人在书中写下的规则之一,现在我们都应该遵循它而不会质疑为什么。在您描述的情况下,单个表听起来就像简单,智能的解决方案。

我肯定不会创建两个表。然后,所有公共字段都将被复制,并且您的所有查询都必须连接或合并两个表。所以真正的问题是,一桌或三桌。但你似乎意识到了这一点。

您没有说明其他字段是什么。如果一个字段的存在或不存在意味着记录类型,那么我有时将该事实用作记录类型指示符而不是创建冗余类型。比如,如果“个人销售”和“商业销售”之间的唯一区别在于商业销售具有填写公司的外键,那么您可以简单地声明您将商业销售定义为填写公司的商品销售在,并没有歧义是可能的。但是如果情况变得稍微复杂一点,这可能是一个陷阱:我看到应用程序说如果a为空并且b = cd / 7 =那么它是记录类型A,否则如果b为null等等。你不能在一个字段上进行一次测试,忘掉它并放入一个记录类型字段。

您始终可以强制执行代码或约束。

我更担心冗余数据会导致一致性问题,然后是不适用的字段。冗余数据会产生各种问题。数据不适用于记录类型?在最坏的情况下,请忽略它。如果这是一次“个人销售”并以某种方式填补了公司,请忽略它或将其视为无效。问题解决了。

答案 3 :(得分:0)

如果有两个不同的实体,“个人销售”和“公司销售”,那么也许您应该有两个表来表示这些实体?

答案 4 :(得分:0)

新闻快讯:数据库无法阻止100%的损坏数据现在,无论你削减它的方式。到目前为止,您只考虑了我称之为1级损坏(如果您使用十六进制编辑器在数据库上写入垃圾,那么0级损坏本质上会发生什么)。

我还没有看到一个可以防止2级损坏的数据库(语法正确的记录,但从整体上看,这意味着有些不正常)。

答案 5 :(得分:0)

保留所有字段在一个表中的PRO就是你摆脱了JOIN,这使你的查询更快。

CONTRA表示您的表格越来越大,这使您的查询速度变慢。

哪一个会对您产生更大影响,完全取决于您的数据分布以及您最常发出的查询。

一般来说,对OLTP系统进行拆分更好,加入更适合数据分析(往往会扫描表格)。

让我们想象一下2场景:

  1. 拆分字段。有1,000,000行,平均行大小为20字节,拆分字段每50行填充一次(即拆分表中的20,000条记录)。

    我们想要像这样查询:

    SELECT  SUM(mainfield + COALESCE(splitfield, 0))
    FROM    maintable
    LEFT JOIN
            splittable
    ON      splitid = mainid
    

    这将需要扫描20,000,000字节和嵌套循环(或散列查找)来查找10,000条记录。

    每次哈希查找大致相当于扫描10行,因此总时间相当于扫描20,000,000 + 10 * 20,000 * 20 = 24,000,000字节

  2. 加入字段。有1,000,000行,平均行大小为24个字节,因此查询将扫描24,000,000个字节。

  3. 如你所见,时代紧迫。

    但是,如果任一参数更改(字段更频繁地填充或更少填充,行大小或多或少等),则一个或另一个解决方案将变得更好。