除非您出于性能原因将denormalized (2) 流派分为三列,否则应该有一个单独的表格来关联歌曲和流派:
CREATE TABLE SongGenres (
song INT NOT NULL REFERENCES Songs (id) ON DELETE CASCADE,
genre VARCHAR(32) NOT NULL,
UNIQUE INDEX (song, genre),
INDEX genres (genre) -- improves performance for getting genre names
) Engine=InnoDB;
这取消了(“Cross Road Blues”可以在“Blues”和“Delta Blues”下归档,但仅此而已)和(想到 A3 的乡村酸屋福音)三种类型的人为限制每首歌。如果您的类型有限,您可能希望创建类型列enumerated。 SongGenres 表简化了获取所有流派:
SELECT UNIQUE genre FROM SongGenres;
或者,您可以进一步规范化并为流派创建一个单独的表:
CREATE TABLE Genres (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(32) NOT NULL,
UNIQUE INDEX (name)
) Engine=InnoDB;
CREATE TABLE SongGenres (
song INT NOT NULL REFERENCES Songs (id) ON DELETE CASCADE,
genre INT NOT NULL REFERENCES Genres (id) ON DELETE RESTRICT,
UNIQUE INDEX (song, genre)
) Engine=InnoDB;
这更加简化了获取所有类型名称的过程(尽管这只是次要优势):
SELECT name FROM Genres;
流派表的主要优势在于数据的正确性:如果有人拼错了流派,则不会在流派表中找到该流派。一个潜在的缺点是它将有效类型限制为表中的类型。当然,给在 SongGenres 上具有 INSERT 权限的用户帐户是有意义的,所以这个限制并不严重。一旦您开始添加新的流派,您将面临与没有流派表时相同的问题:错别字。不要添加在 Genres 表中找不到的新流派,而是寻找类似的流派(例如使用 Levenshtein distance 或 SOUNDS LIKE),如果找到,询问用户是否想用一个流派替换该流派找到的内容或保留原始流派(并将其添加到流派列表中)。
这是第一种情况下的数据(两个表,Songs 和 SongGenres):
mysql> SELECT * FROM Songs;
+----+---------+--------+----
|编号 |标题 |艺术家 | ...
+----+---------+--------+----
| 1 |十字路口蓝调 | ...
| 2 |山谷中的和平| ...
+----+---------+--------+----
2 行(0.00 秒)
mysql> SELECT * FROM SongGenres;
+--------+-------------+
|歌曲 |流派 |
+--------+-------------+
| 2 |酸 |
| 1 |蓝调 |
| 2 |国家 |
| 1 |三角洲蓝调 |
| 2 |福音 |
| 2 |房子|
| 2 |技术 |
+--------+-------------+
一组 7 行(0.00 秒)
mysql> SELECT s.title, sg.genre FROM Songs AS s JOIN SongGenres AS sg ON s.id=sg.song;
+---------+-------------+
|标题 |流派 |
+---------+-------------+
|十字路口蓝调 |蓝调 |
|十字路口蓝调 |三角洲蓝调 |
|山谷中的和平|酸 |
|山谷中的和平|国家 |
|山谷中的和平|福音 |
|山谷中的和平|房子|
|山谷中的和平|技术 |
+---------+-------------+
一组 7 行(0.00 秒)
使用单独的 Genres 表,Songs 中的数据看起来是一样的,但在其他表中我们会有类似的内容:
mysql> SELECT * FROM Genres;
+----+--------------+
|编号 |姓名 |
+----+--------------+
| 1 |酸 |
| 2 |蓝调 |
| 3 |经典|
| 4 |国家 |
| 5 |三角洲蓝调 |
| 6 |民间|
| 7 |福音 |
| 8 |嘻哈 |
| 9 |房子|
...
| 18 |技术 |
+----+--------------+
18 行一组(0.00 秒)
mysql> SELECT * FROM SongGenres;
+--------+-------+
|歌曲 |流派 |
+--------+-------+
| 1 | 2 |
| 1 | 5 |
| 2 | 1 |
| 2 | 4 |
| 2 | 7 |
| 2 | 9 |
| 2 | 18 |
+--------+-------+
一组 7 行(0.00 秒)
mysql> SELECT s.title, g.name 作为流派
-> FROM Songs AS s
-> JOIN SongGenres AS sg ON s.id=sg.song
-> 加入流派作为 g ON sg.genre=g.id;
+----------+-------------+
|标题 |流派 |
+---------+-------------+
|十字路口蓝调 |蓝调 |
|十字路口蓝调 |三角洲蓝调 |
|山谷中的和平|酸 |
|山谷中的和平|国家 |
|山谷中的和平|福音 |
|山谷中的和平|房子|
|山谷中的和平|技术 |
+---------+-------------+
一组 7 行(0.00 秒)