1

我对数据库设计相当陌生,我试图弄清楚以下模式将如何工作。

我想有一个用户数据库,理想情况下应该在一个表中。然后对于每个用户,我需要存储一个加权的标签列表,以后可以扩展。例如:

User1: dnb (+3), dubstep (+1), classical (-2), rnb (+1)

User2: rnb (+2), hiphop (+4), jazz (-3), classical (-1)

其中括号中的数字是每个用户的标签“权重”。我也希望能够随时为每个用户添加更多标签

4

1 回答 1

2

这相当简单。您想要表示有关用户和标签的信息,每个用户都有一个或多个标签,每个标签都有一个权重。鉴于该信息,您有一个多对多的基数,您可以用三个表来表示它:

Users(UserID, Name, Surname, DateOfBirth);
Tags(TagID, TagName);
UsersAndTags(UserID, TagID, TagWeight);

如果您对为什么这是多对多关系感到困惑:

  • 用户可以有一个或多个与他/她自己相关联的标签。
  • 一个标签可以与许多用户相关联。

该图看起来像这样:

  • 用户 * ---------------- * 标签

我将在下面提供更详细的解释。


您可以表示关系中的所有内容,例如:

+--------+------+---------+-------------+-----+-----------+
| UserID | Name | Surname | DateOfBirth | Tag | TagWeight |
+--------+------+---------+-------------+-----+-----------+

但这是一个不好的关系,因为您在单个表中表示异构信息,这样做会导致不一致。考虑以下内容(使用主键 UserID 和 Tag):

+--------+------+--------------------+-------------+--------+-----------+
| UserID | Name |      Surname       | DateOfBirth |  Tag   | TagWeight |
+--------+------+--------------------+-------------+--------+-----------+
|   1230 | Ana  | Patson             | 12/01/1980  | music  | -1        |
|   2300 | Mike | Johnson            | 01/03/1979  | art    | +3        |
|   2300 | Mike | Johnson            | 01/03/1979  | sports | +1        |
|   2300 | Mike | Johnson            | 01/03/1979  | hiphop | -4        |
|   2300 | Mike | Johnson            | 01/03/1979  | rnb    | -2        |
|   1230 | Ana  | Patson             | 12/01/1980  | rnb    | +1        |
|   1230 | Ana  | Patson             | 12/01/1980  | hiphop | +3        |
|   1230 | Ana  | Patson             | 12/01/1980  | dnb    |  0        |
+--------+------+--------------------+-------------+--------+-----------+

我们在此表中有各种不一致之处,特别是:

  • 冗余:我们有重复多次的数据
  • 删除异常:如果我们要删除用户没有与自己关联的标签的事实,那么我们将不得不将她从数据库中完全删除,因为Ana这是我们主键的一部分。这可能不是我们想要的。ID 1230Tag
  • 如果 Ana 结婚并采用她丈夫的姓氏,则更新异常,那么我们必须多次更新与她的条目对应的所有行。
  • 插入异常我们无法插入没有标签的新用户

规范化是我们消除上述不一致的过程。最常见的有:第一范式(简称1NF)、第二范式(2NF)、第三范式(3NF)、Boyce-Codd范式(BCNF)。还有第四个范式(以及第五个和第六个),我不在这里讨论。

如果所有属性(即列)都是原子的,则表处于第一范式。这意味着每个属性必须代表一个事实,而不是事实的集合。我们的桌子在 1NF 中。

第二范式处理复合主键(即具有多个属性的主键,我们所拥有的)并声明如果每个其他属性(即不属于复合主键的属性)必须依赖,则关系处于 2NF在整个主键上。让我们看看我们的桌子。乍一看,我们可能认为这种关系符合 2NF,因为给定复合主键{UserID, Tag},我们可以确定表中的所有其他属性。然而,NameSurnameDateOfBirth取决于UserID而不是取决于Tag哪个也是我们的复合键的一部分。因此这种关系不符合2NF

第三范式表明,如果表中的每个属性都直接依赖于键,则关系是 3NF。这消除了一个属性可以依赖于另一个属性的事实,而另一个属性本身依赖于密钥(即 2NF 允许的传递性规则)。我们已经说过我们的表不符合 2NF,因此不能符合 3NF。

范式是累积的,这意味着如果我们的表在 3NF 中,那么它也在 2NF 和 1NF 中。

我们一直在谈论的这些依赖项称为功能依赖项(FD)。函数依赖的写法类似于X -> YX 在函数上决定 Y(或 Y 由 X 决定),这仅仅意味着 X 的值决定 Y 的值(我们对 X 有相同的值,我们必须有Y 的值相同)。在我们的情况下,只要我们有UserID 1230,我们就有Name AnaandSurname PatsonDateOfBirth 12/01/1980。因此,我们有以下功能依赖:

UserID -> Name, Surname, DateOfBirth.

更正式地说,3NF 声明对于每个 FD X -> Y,X 要么是密钥(或包含密钥,即密钥的超集),要么Y 是密钥的一部分。我提到这一点是因为这是关于 3NF 的一个重要事实,它与 BCNF 不同。BCNF声明对于每个 FD X -> YX 是一个密钥。

我们关系中的其他功能依赖项:

UserID, Tag -> TagWeight

UserID, Tag -> All the attributes

我们可以看到第一个FDUser -> Name, Surname, DateOfBirth不是key(UserID不是key,只是key的一部分)。这是一个问题,这就是导致我们关系异常的原因。当我们标准化我们的关系时,我们会根据函数依赖关系进行分解。从第一个 FD 我们得到(R 代表关系,即表):

R1(UserID, Name, Surname, DateOfBirth) 

从第二个我们得到:

R2(UserID, Tag, TagWeight)

现在好多了。我们在不同的表中表示有关每个用户的事实,在另一个表中表示有关用户和标签关联的事实。这符合 3NF 和 BCNF。

您可能想要添加第三个表,并将其中的属性R3(TagID, Tag)替换为(并且您会得到 3 个表,就像我在开始时所说的那样)。TagR2TagID

我还没有谈到重要的无损连接分解,我鼓励您阅读。我希望这有帮助。

于 2013-10-19T13:34:12.660 回答