如果在数据库建模期间忽略了弱实体,会出现什么问题?

时间:2013-01-06 15:13:57

标签: database database-design erd

我在进行数据库建模时从不使用弱实体,直到现在看来还好。我通常通过为每个实体提供主要(自动生成)密钥来忽略整个问题。

然而,我遇到了一些posts,提到如果某些实体的存在完全取决于其他实体,那么它们应该是弱的。

但另一方面,有些人将弱实体称为一个集合,它没有足够的属性来形成主键。那么这意味着我的数据库中的所有实体在我给它们自动递增密钥之前首先是弱的。

有人可以概述弱实体的重要性以及不使用它们的后果是什么?为什么我们不给每个实体一个主要的自动生成密钥并使其变得强大?

更新

也许有人可以解释为什么弱实体应该通过父实体的主键+标识符来识别,而不是创建代理键并使用外键将其与父实体相关联(在更新和删除时进行级联更改) ?

3 个答案:

答案 0 :(得分:2)

以多个订单行项目作为示例。弱实体将是存储在其自己的表中的各个行项。它们的主键可以是订单的主键,加上一个简单的整数(例如1,2,3,它只在订单中是唯一的。)因此,它们实际上没有自己的主键作为唯一编号列,它们的键跨越两列,并且只有这样才是唯一的。

如果订单被删除,订单行项目应该被删除 - 它们没有任何意义。正是这种联系使得它们变得脆弱 - 被删除的一件事应该删除另一件事。

如果您为每个订单行项目提供了自己的主键,您仍然需要将它们与订单项相关联,这意味着为订单商品添加外键或者具有交叉引用表。 (您可能还需要知道订单中的行项目编号,这意味着添加一个简单的整数列...此时您已添加足够的密钥而没有自动生成的密钥。)对于设计模式对于拥有的子项目,这些替代方案中的任何一种都有点过分。

使用复杂的主键还会强制执行订单和订单项之间的关系,因为此架构不允许您将订单项分配给多个订单。

另一个考虑因素是您可以根据订单商品主键对订单和订单行进行分片,因为两个表都有该密钥。 (基于主键,通常比常规列更容易进行分片。)


分层遏制并不总是你想要的;但是,它是一种常见的模式,很清楚它,并且在这种情况下可以使用复合键。在这里,使用包含订单项作为子项目(即包含)的订单商品,我们不仅仅说订单项的订单项数量是1到多,而且这些订单项是拥有的,并且不存在于订单之外 - 该订单项构成了一个订单对象。

与此一致,我们明确地不会为(所有)行项目(作为一个组一起)管理单独的密钥空间,而是借用和扩展订单的密钥空间。而不是要求系统为行项目维护单独的密钥空间,而是手动(即不太正式)将外键关系维护回订单,并且还要单独维护整数行项目(从外部引用顺序),我们可以要求系统确保整个组合键的唯一性,其中包括订单中的行项目编号。

当然,您将无法添加与订单无关的订单项,但另外,使用复合子键,您也无法添加与其他订单重叠的订单项。 (例如,它不允许您为同一订单添加两个订单项#3)。

这迫使订单项的生产者和消费者将其视为包含在订单内部和部分订单中,而不是作为独立项目,或者换句话说,通过订单来引用订单项,或者换句话说,通过引用其中一个订单项来获取“免费”订单的引用。 (并且因为您还可以将订单作为此类外键的一部分引用,您还可以单独使用复合外键的订单部分进行分组或加入。)

答案 1 :(得分:1)

我最近参与了一个项目,该项目必须管理大量的湖泊读数数据样本。在这个项目中,我们有类似于下面的表格,其中records是按位置和上传者的湖泊读数的集合,而samples包含实际的湖泊读数 - 温度和强度等。

CREATE TABLE records(
    email TEXT REFERENCES users(email),
    lat DECIMAL,
    lon DECIMAL,
    depth TEXT,
    upload_date TIMESTAMP,
    comment TEXT,
    PRIMARY KEY (upload_date,email)
);

CREATE TABLE samples(
    date_taken TIMESTAMP,
    temp DECIMAL,
    intensity DECIMAL,
    upload_date TIMESTAMP,
    email TEXT,
    PRIMARY KEY(date_taken,upload_date,email),
    FOREIGN KEY (upload_date,email) REFERENCES records(upload_date,email)
);

samples被建模为弱实体,取决于records。如您所知,这意味着所有外键都继承自records并用于标识samples中的单个行。但是,如果我们决定将其变成一个实体,会发生什么?好吧,你可以用几种不同的方式来看待它,或者:

  1. 来自records的主键不会出现在samplessamples中 我们必须分配某种任意的自动增量类型 ID,正如您所建议的那样。每条记录包含数千个样本,用户可以想到 样本作为他们在现场记录的记录的一部分。他们 期望按记录​​浏览样本,所以我们会非常大 records表没有明显映射到它们所属的记录 在现实生活中。

  2. 或者我们根本不将模型作为弱实体,但要认识到这一点 它需要能够用upload_date行来识别自己,所以我们 分配emailupload_date。如果我们做这两个条目 外键,然后我们刚刚实现了一个弱实体 它。如果我们不这样做,那么我们的应用层必须负责 检查以确保每个emailrecords都是 也出现在samples,而不是数据库中。

  3. 在这种情况下,使upload_date成为弱实体(包括其主键中的外键)是最简单的选择(并且最有意义)。

    <强>摘要

    当实体在现实生活中实际上很弱时,你应该将实体建模为弱者。如果你有一个实体需要一个不同的密钥的一部分来标识自己(有一个外键是其主键的一部分),那么它可能很弱。

    您可以重新构建系统以避免使用弱实体吗?可能,如果我们想要有无关联的样本,那么我们需要能够使它们email和{{1}}为空,这意味着它们不会在主键中,也不会是弱实体。我们必须做我在1中描述的事情。

答案 2 :(得分:0)

主键必须是唯一的。永远。这里的所有都是它的。如果表中的数据没有提供,那么你自然会创建一个代理键。

现在是什么。自然键由一个或多个现有列组成,而代理键是额外添加的列,通常是自动增量。

自然键的一个很好的例子是countries表中的ISO国家/地区代码。你在这里添加一个自动增量列没有任何好处。相反,您可以在某些查询中保存自己不要在countries表中加入,因为您已经拥有ISO代码。

错误的一个,contacts表中的名称(或多个列)。这就是为什么在这种情况下使用代理键更好的原因。

这就是我对它的看法,我很少 - 如果有的话 - 遇到任何可疑的布局问题。

实用提示:您从不在构成主键的列上运行UPDATE。您将删除该行并使用新值重新插入该行。这可以为你节省很多麻烦。