我对数据库设计还不熟悉,我试图弄清楚以下架构的工作原理。
我想拥有一个用户数据库,理想情况下会在一个表中。然后,对于每个用户,我需要存储一个加权的标签列表,以后可以扩展。例如:
User1: dnb (+3), dubstep (+1), classical (-2), rnb (+1)
User2: rnb (+2), hiphop (+4), jazz (-3), classical (-1)
其中括号内的数字是每个用户的标记的“权重”。我希望能够随时为每个用户添加更多标签
答案 0 :(得分: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 1230
没有与自己关联的标签这一事实,那么我们必须将她从数据库中完全删除因为Tag
是我们主键的一部分。这可能不是我们想要的。规范化是一个删除上述不一致的过程。最常见的是:第一范式(缩写为1NF),第二范式(2NF),第三范式(3NF), Boyce-Codd普通形式(BCNF)。还有第四种正常形式(以及第五种和第六种形式),我没有进入这里。
如果所有属性(即列)都是原子,则表格为第一范式。这意味着每个属性必须代表一个事实,而不是事实的集合。我们的桌子在1NF。
第二范式处理复合主键(即具有多个属性的主键,我们拥有的主键),并声明如果每个其他属性(即不属于复合主键的属性)必须依赖于 整个 主键。我们来看看我们的表格。乍一看,我们可能认为这种关系符合2NF,因为给定复合主键{UserID, Tag}
,我们可以确定表中的所有其他属性。但是,Name
,Surname
和DateOfBirth
仅依赖UserID
,而不依赖Tag
,这也是我们的复合键的一部分。因此,这种关系不符合2NF
。
第三范式表示如果表中的每个属性都依赖于直接,则关系为3NF。这消除了属性可以依赖于另一个属性的事实,该属性本身取决于密钥(即2NF允许的传递性规则)。我们已经说过我们的桌子不符合2NF,因此不能与3NF一致。
正常形式是累积,这意味着如果我们的表格在3NF,那么它也在2NF和1NF。
我们一直在谈论的这些依赖关系被称为功能依赖(FD)。函数依赖关系写成X -> Y
,你读它为 X在功能上确定Y (或Y由X确定),这只是意味着X的值决定了Y的值(我们在X中具有相同的值,我们必须具有相同的Y值。在我们的情况下,只要我们UserID
1230
我们就有Name
Ana
和Surname
Patson
以及DateOfBirth
{{1} }。因此,我们有以下功能依赖:
12/01/1980
更正式地说,3NF声明对于每个FD UserID -> Name, Surname, DateOfBirth.
,X是键(或包含键,即键的超集)或 Y是键的一部分。我之所以提到这一点,是因为它是一个关于3NF的重要事实,它将它与BCNF区分开来。 BCNF 指出,每个FD X -> Y
X都是关键。
我们关系中的其他功能依赖:
X -> Y
我们可以看到第一个FD UserID, Tag -> TagWeight
UserID, Tag -> All the attributes
不是密钥(UserID不是密钥,只是密钥的一部分)。这是一个问题,这就是导致我们关系出现异常的原因。当我们规范化我们的关系时,我们会根据函数依赖关系进行分解。从我们获得的第一个FD(R代表关系即表):
User -> Name, Surname, DateOfBirth
从第二个我们得到:
R1(UserID, Name, Surname, DateOfBirth)
现在情况好多了。我们在不同的表中表示关于每个用户的事实以及关于另一个表中的用户和标记关联的事实。这符合3NF和BCNF。
您可能希望添加第三个R2(UserID, Tag, TagWeight)
表,并将R3(TagID, Tag)
中的Tag
属性替换为R2
(并且您可以获得3个表,就像我说的那样)一开始)。
我还没有触及无损连接分解,这很重要,我鼓励你阅读。我希望这会有所帮助。