I have a movie
in a MySQL database. A movie
contains data attributes that will never change, such as the following:
It's one single row in a table.
But I want to give my user's complete customization over this information in case something is incorrect according to them or if they just want to modify how the data is displayed in some way. I don't care why, I just want to give my users an option to do what they want.
Let's say User #1 wants to change the Title for them to be "12 Monkeys (Shelf 1)" and that's all they change.
And let's say User #2 wants to change DVD to Digital Copy instead.
And let's say User #3 wants to change the Title for them to be "Twelve Monkeys" because it's the alternative title.
etc.
My question is, how do I store just that one change to that one field for that username only, without modifying the original data? In a separate identical table with all fields the exact same data except that one field? Or can I just store that one single change (Title for example) somewhere and refer back to the movie
data for the rest?
What's the proper way to design this, especially if I have 1000's of users making customized data modifications mostly on just one or two fields?
Instead of a single row for each movie, use an attribute-value table. Then add an additional field to this that specifies the user, which would be 0
for the original default. So the table looks like:
MovieID UserID Attribute Value
1 0 Title 12 Monkeys
1 0 Format DVD
1 1 Title Twelve Monkeys
Then a query to get the title would look like:
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'
Some database designers also like to use an AttributeID
rather than a string as the attribute name, and a separate table that maps attribute names to IDs.
I suggest there is no 'proper' way. But you might like this...
Movie
table stays as is. (I assume there is an id
.)UserMovie
with the same columns except:
id
are NULL
user NOT NULL
PRIMARY KEY(id, user)
When a user modifies something, use INSERT INTO UserMovie .. ON DUPLICATE KEY UPDATE ..
to change whatever field(s) he wants to set. Note that IODKU will INSERT
a new row if none exists, or UPDATE
the existing row (because the user is modifying another column). For example, to override just the "title" for id=$id,
INSERT INTO UserMovie
(id, title)
VALUES
($id, '$title')
ON DUPLICATE KEY UPDATE
title = '$title';
When a user wants to see what he has,
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;
The COALESCE
quietly pics either u.xxx
if NOT NULL
, or m.xxx
.
This design has the advantage of being very compact. (NULLs
take almost no space.)
If a user changes the "title" twice, only the last version is kept.
To "revert" the title:
UPDATE UserMovie SET title = NULL
WHERE id = $id
AND user = $user;
(Sure, this could leave a row of all NULLs
, but the rest of the code still works.)
My first thought is, Why would you want to do this?
My second thought is to have a customizations
table something like
+--------+---------+-------------+------------+
| userid | barcode | column_name | custom_val |
+--------+---------+-------------+------------+
then, when a user queries the system, look up their userid and the column_name in the customizations
table to find the alternative display value.
This would allow a user to replace one value in one row. To replace one value in all rows where it exists would be a much more difficult proposition.
A good design is not perfect for all situations. However there is a perfect design for a situation.
Ask yourself again: 1) what the purpose of this design and, 2) how are you going to retrieve the data from the design.
According to your question, if a movie never changes its attributes, a flat one-row table is perfect:
table: movie
id | barcode | format_id | runtime | disc | year_made | title
---+--------------+-----------+---------+------+-----------+-----------
1 | 025192018626 | 1 | 121 | 1 | 1995 | 12 Monkeys
And you might need a foreign table movie_format
table: movie_format
id | format
---+-------
1 | DVD
Above design is very speedy for search.
Now you want to save any changes, or alternative information, however not sure what they are. In this case, a meta table is more suitable. Meta table is usually perfect if you only need to display according to a primary key (a movie) and not used for a search:
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 | 十二头傻猴子
You can make movie_meta.meta as enum so that you do not need to worry about a new lookup table
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With