【发布时间】:2022-11-19 00:34:05
【问题描述】:
这是一个古老且反复出现的问题,但请原谅我自己没有找到正确的答案,因为每个人的情况或多或少都是独一无二的。除此之外,我需要少一点理论,多一点实践。
谈正事。我知道在单个表字段中存储多个逗号分隔值是众所周知的糟糕设计,因为它会影响性能并破坏关系。尽管如此,直到今天仍有很多人求助于它,因为他们不知道更好或者只是懒惰。直到现在我终于决定让事情变得更好,我的情况也没有什么不同。除了我不确定如何。
情况是这样的。假设我有一张电影表:
+------------------------------+
| id | name | year |
+------------------------------+
| 1 | Alien | 1979 |
| 2 | Breakfast Club | 1985 |
| 3 | First Blood | 1982 |
+------------------------------+
现在我需要以某种方式将流派存储在某个地方。显然,我将需要一个单独的流派表,因为将它插入同一个表是不行的。但是如何构造呢?我只能考虑两种方式:使用外键或者布尔类型字段,各有优缺点。
让我们观察第一个:
+---------------------+
| movie_id | genre_id |
|----------+----------+
| 1 | 2 |
| 1 | 4 |
| 3 | 1 |
+----------+----------|
所以,这里的问题是每个流派惩罚的一行。意思是,如果我需要存储不止一种类型,我将不得不像这样运行多个插入查询:
foreach($genres as $genre_id)
{
$db->query("INSERT INTO genres (movie_id, genre_id) VALUES ('$movie_id', '$genre_id');
}
那看起来不漂亮。我的意思是,如果我错了请纠正我,但这不是过多的数据库调用吗?而且,显然,这将与引入的流派数量有关。我还应该怎么做?如果有更好的方法,请告诉。
现在第二种情况:
+----------+-----------+--------+-------+----------
| movie_id | adventure | comedy | drama | horror ...
+----------+-----------+--------+-------+----------
| 1 | 0 | 1 | 0 | 1 ...
+----------+-----------+--------+-------+----------
| 3 | 1 | 0 | 0 | 0 ...
+----------+-----------+--------+-------+----------
好的,这样更好。或者是吗?当然,这将允许我运行单个插入,除了现在它以相当复杂的查询构建过程作为回报:
$query1 = "INSERT INTO genres (movie_id, ";
$query2 = " VALUES ('$movie_id', ";
foreach($genres as $key => $value)
{
if($value)
{
$query1 .= "$key, ";
$query2 .= "'$value', ";
}
}
$query = rtrim($query1, ", ") . ")" . rtrim($query2, ", ") . ")";
$db->query($query);
但我想它有效。不过,我不知道这是否非常实用。
坦率地说,我不喜欢这两种方法,我觉得必须有第三种更优雅的解决方案,但我现在无法想象。
【问题讨论】:
-
“好吧,这样更好。还是这样?”- 它实际上比逗号分隔值更糟糕。字符串至少可以被解析/操作/等等。即使它不是很好。但存储数据作为图式(流派作为列名)不仅难以读该数据但需要您修改模式任何时候您想更改该数据。您的第一个想法看起来像标准的多对多关系。认为它“看起来不漂亮”并不是避免它的有效技术理由。
-
除非一部电影有几十种类型,否则我不会担心每种类型都有多个插入。您不会经常添加电影,以至于这种开销很大。
标签: php mysql normalization