我对数据库设计相当陌生,我试图弄清楚以下模式将如何工作。
我想有一个用户数据库,理想情况下应该在一个表中。然后对于每个用户,我需要存储一个加权的标签列表,以后可以扩展。例如:
User1: dnb (+3), dubstep (+1), classical (-2), rnb (+1)
User2: rnb (+2), hiphop (+4), jazz (-3), classical (-1)
其中括号中的数字是每个用户的标签“权重”。我也希望能够随时为每个用户添加更多标签
这相当简单。您想要表示有关用户和标签的信息,每个用户都有一个或多个标签,每个标签都有一个权重。鉴于该信息,您有一个多对多的基数,您可以用三个表来表示它:
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在整个主键上。让我们看看我们的桌子。乍一看,我们可能认为这种关系符合 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
andSurname
Patson
和DateOfBirth
12/01/1980
。因此,我们有以下功能依赖:
UserID -> Name, Surname, DateOfBirth.
更正式地说,3NF 声明对于每个 FD X -> Y
,X 要么是密钥(或包含密钥,即密钥的超集),要么Y 是密钥的一部分。我提到这一点是因为这是关于 3NF 的一个重要事实,它与 BCNF 不同。BCNF声明对于每个 FD X -> Y
X 是一个密钥。
我们关系中的其他功能依赖项:
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 个表,就像我在开始时所说的那样)。Tag
R2
TagID
我还没有谈到重要的无损连接分解,我鼓励您阅读。我希望这有帮助。