在我正在设计的数据库中,有一个相当中心的表格,代表已出售或待售的东西。它区分个人销售(如eBay)和适当公司的销售。这意味着实际上有1个或两个字段不同于两种情况......例如,一个字段仅在一种情况下使用,另一个字段在一种情况下是可选的但在另一种情况下是强制性的。 如果有更多的专业知识,那么拥有核心表格然后两个表格与特定案例相关的字段是明智的。但是在这里,创建两个表只是为了包含一个字段加上对核心表的引用,这对于查询设计人员和数据库软件来说既美观又糟糕。
你怎么看?通过使用具有弱约束的单个表来稍微弯曲规则是否可以 - 这意味着DB不能100%防止添加不一致的数据(以非常有限的方式) - 或者我是否将其吸收并创建看起来愚蠢的1字段表?答案 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,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
字节
加入字段。有1,000,000
行,平均行大小为24
个字节,因此查询将扫描24,000,000
个字节。
如你所见,时代紧迫。
但是,如果任一参数更改(字段更频繁地填充或更少填充,行大小或多或少等),则一个或另一个解决方案将变得更好。