将此数据存储在 MySQL 模式中的正确方法是什么?
What's the proper way to store this data in a MySQL schema?
我在 MySQL 数据库中有一个 movie
。一个movie
包含永远不会改变的数据属性,例如:
- 条码:025192018626
- 格式:DVD
- 运行时长:121 分钟
- 光盘:1
- 标题:12 只猴子
- 年份:1995
它是 table 中的一行。
但是我想让我的用户完全自定义此信息,以防他们认为有什么不正确的地方,或者如果他们只是想以某种方式修改数据的显示方式.我不在乎为什么,我只是想给我的用户一个选择,让他们做他们想做的事。
假设用户 #1 想要将他们的标题更改为“12 Monkeys (Shelf 1)”,而这就是他们所做的全部更改。
假设用户 #2 想将 DVD 改为数字拷贝。
假设用户 #3 想要将他们的标题更改为 "Twelve Monkeys",因为它是备用标题。
等等
我的问题是,如何在不修改原始数据的情况下,仅只存储该用户名对那个字段的更改?在一个单独的相同 table 中,除了 one 字段外,所有字段都具有完全相同的数据?或者我可以只将一个更改(例如标题)存储在某个地方,然后参考 movie
数据以获取其余信息吗?
设计这个的正确方法是什么,特别是如果我有 1000 名用户主要在一个或两个字段上进行自定义数据修改?
我的第一个想法是,你为什么要这样做?
我的第二个想法是 customizations
table
+--------+---------+-------------+------------+
| userid | barcode | column_name | custom_val |
+--------+---------+-------------+------------+
然后,当用户查询系统时,在 customizations
table 中查找他们的用户 ID 和 column_name 以找到替代显示值。
这将允许用户替换一行中的一个值。要在所有存在的行中替换一个值将是一个更困难的命题。
不是每部电影一行,而是使用 attribute-value table。然后向其中添加一个额外的字段来指定用户,对于原始默认值,这将是 0
。所以 table 看起来像:
MovieID UserID Attribute Value
1 0 Title 12 Monkeys
1 0 Format DVD
1 1 Title Twelve Monkeys
然后获取标题的查询如下所示:
SELECT MovieID, IFNULL(my.Value, default.Value) AS title
FROM movies AS default
LEFT JOIN movies AS my ON default.MovieID = my.MovieID AND my.Attribute = 'Title' AND my.userID = @user
WHERE default.UserID = 0 AND default.Attribute = 'Title'
一些数据库设计者还喜欢使用 AttributeID
而不是字符串作为属性名称,并使用单独的 table 将属性名称映射到 ID。
我建议没有'proper'方法。但你可能会喜欢这个...
- 您的
Movie
table 保持原样。 (我假设有一个 id
。)
- 另一个 table、
UserMovie
具有相同的列,除了:
- 除
id
以外的所有列都是NULL
- 它还有另一列:
user NOT NULL
PRIMARY KEY(id, user)
当用户修改某些内容时,使用INSERT INTO UserMovie .. ON DUPLICATE KEY UPDATE ..
更改他想要设置的任何字段。请注意,如果 none 存在,IODKU 将 INSERT
一个新行,或者 UPDATE
现有行(因为用户正在修改另一列)。例如,仅覆盖 "title" for id=$id,
INSERT INTO UserMovie
(id, title)
VALUES
($id, '$title')
ON DUPLICATE KEY UPDATE
title = '$title';
当用户想要查看他拥有的内容时,
SELECT coalesce(u.title, m.title) AS title,
coalesce(u.format, m.format) AS format,
coalesce...
FROM Movie AS m
LEFT JOIN UserMovie AS u
ON u.id = m.id
AND u.user = $user
WHERE m.id = $id;
COALESCE
静静地拍照 u.xxx
if NOT NULL
或 m.xxx
.
这种设计的优点是非常紧凑。 (NULLs
几乎没有space。)
如果用户更改 "title" 两次,则仅保留最后一个版本。
给"revert"标题:
UPDATE UserMovie SET title = NULL
WHERE id = $id
AND user = $user;
(当然,这可能会留下一行所有 NULLs
,但其余代码仍然有效。)
好的设计并不适用于所有情况。然而,有一个针对特定情况的完美设计。
再问问自己:1) 这个设计的目的是什么,2) 你打算如何从设计中检索数据。
根据你的问题,如果一部电影从不改变它的属性,那么单行 table 是完美的:
table: movie
id | barcode | format_id | runtime | disc | year_made | title
---+--------------+-----------+---------+------+-----------+-----------
1 | 025192018626 | 1 | 121 | 1 | 1995 | 12 Monkeys
你可能需要一个外国的 table movie_format
table: movie_format
id | format
---+-------
1 | DVD
以上设计搜索速度非常快
现在您想保存任何更改或替代信息,但不确定它们是什么。在这种情况下,meta table 更 suitable。 Meta table 如果你只需要根据主键(电影)显示而不用于搜索,通常是完美的:
table: movie_meta
id | movie_id | user_id | created | meta | info
---+----------+---------+---------------------+-----------+---------
1 | 1 | 123 | 2016-01-28 11:22:33 | format_id | 2
2 | 1 | 456 | 2016-01-28 11:55:33 | disc | 3
5 | 1 | 666 | 2016-07-14 12:58:55 | title | 十二头傻猴子
您可以将 movie_meta.meta 作为枚举,这样您就不必担心新的查找 table
我在 MySQL 数据库中有一个 movie
。一个movie
包含永远不会改变的数据属性,例如:
- 条码:025192018626
- 格式:DVD
- 运行时长:121 分钟
- 光盘:1
- 标题:12 只猴子
- 年份:1995
它是 table 中的一行。
但是我想让我的用户完全自定义此信息,以防他们认为有什么不正确的地方,或者如果他们只是想以某种方式修改数据的显示方式.我不在乎为什么,我只是想给我的用户一个选择,让他们做他们想做的事。
假设用户 #1 想要将他们的标题更改为“12 Monkeys (Shelf 1)”,而这就是他们所做的全部更改。
假设用户 #2 想将 DVD 改为数字拷贝。
假设用户 #3 想要将他们的标题更改为 "Twelve Monkeys",因为它是备用标题。
等等
我的问题是,如何在不修改原始数据的情况下,仅只存储该用户名对那个字段的更改?在一个单独的相同 table 中,除了 one 字段外,所有字段都具有完全相同的数据?或者我可以只将一个更改(例如标题)存储在某个地方,然后参考 movie
数据以获取其余信息吗?
设计这个的正确方法是什么,特别是如果我有 1000 名用户主要在一个或两个字段上进行自定义数据修改?
我的第一个想法是,你为什么要这样做?
我的第二个想法是 customizations
table
+--------+---------+-------------+------------+
| userid | barcode | column_name | custom_val |
+--------+---------+-------------+------------+
然后,当用户查询系统时,在 customizations
table 中查找他们的用户 ID 和 column_name 以找到替代显示值。
这将允许用户替换一行中的一个值。要在所有存在的行中替换一个值将是一个更困难的命题。
不是每部电影一行,而是使用 attribute-value table。然后向其中添加一个额外的字段来指定用户,对于原始默认值,这将是 0
。所以 table 看起来像:
MovieID UserID Attribute Value
1 0 Title 12 Monkeys
1 0 Format DVD
1 1 Title Twelve Monkeys
然后获取标题的查询如下所示:
SELECT MovieID, IFNULL(my.Value, default.Value) AS title
FROM movies AS default
LEFT JOIN movies AS my ON default.MovieID = my.MovieID AND my.Attribute = 'Title' AND my.userID = @user
WHERE default.UserID = 0 AND default.Attribute = 'Title'
一些数据库设计者还喜欢使用 AttributeID
而不是字符串作为属性名称,并使用单独的 table 将属性名称映射到 ID。
我建议没有'proper'方法。但你可能会喜欢这个...
- 您的
Movie
table 保持原样。 (我假设有一个id
。) - 另一个 table、
UserMovie
具有相同的列,除了:- 除
id
以外的所有列都是NULL
- 它还有另一列:
user NOT NULL
PRIMARY KEY(id, user)
- 除
当用户修改某些内容时,使用INSERT INTO UserMovie .. ON DUPLICATE KEY UPDATE ..
更改他想要设置的任何字段。请注意,如果 none 存在,IODKU 将 INSERT
一个新行,或者 UPDATE
现有行(因为用户正在修改另一列)。例如,仅覆盖 "title" for id=$id,
INSERT INTO UserMovie
(id, title)
VALUES
($id, '$title')
ON DUPLICATE KEY UPDATE
title = '$title';
当用户想要查看他拥有的内容时,
SELECT coalesce(u.title, m.title) AS title,
coalesce(u.format, m.format) AS format,
coalesce...
FROM Movie AS m
LEFT JOIN UserMovie AS u
ON u.id = m.id
AND u.user = $user
WHERE m.id = $id;
COALESCE
静静地拍照 u.xxx
if NOT NULL
或 m.xxx
.
这种设计的优点是非常紧凑。 (NULLs
几乎没有space。)
如果用户更改 "title" 两次,则仅保留最后一个版本。
给"revert"标题:
UPDATE UserMovie SET title = NULL
WHERE id = $id
AND user = $user;
(当然,这可能会留下一行所有 NULLs
,但其余代码仍然有效。)
好的设计并不适用于所有情况。然而,有一个针对特定情况的完美设计。
再问问自己:1) 这个设计的目的是什么,2) 你打算如何从设计中检索数据。
根据你的问题,如果一部电影从不改变它的属性,那么单行 table 是完美的:
table: movie
id | barcode | format_id | runtime | disc | year_made | title
---+--------------+-----------+---------+------+-----------+-----------
1 | 025192018626 | 1 | 121 | 1 | 1995 | 12 Monkeys
你可能需要一个外国的 table movie_format
table: movie_format
id | format
---+-------
1 | DVD
以上设计搜索速度非常快
现在您想保存任何更改或替代信息,但不确定它们是什么。在这种情况下,meta table 更 suitable。 Meta table 如果你只需要根据主键(电影)显示而不用于搜索,通常是完美的:
table: movie_meta
id | movie_id | user_id | created | meta | info
---+----------+---------+---------------------+-----------+---------
1 | 1 | 123 | 2016-01-28 11:22:33 | format_id | 2
2 | 1 | 456 | 2016-01-28 11:55:33 | disc | 3
5 | 1 | 666 | 2016-07-14 12:58:55 | title | 十二头傻猴子
您可以将 movie_meta.meta 作为枚举,这样您就不必担心新的查找 table