Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the proper way to store this data in a MySQL schema?

I have a movie in a MySQL database. A movie contains data attributes that will never change, such as the following:

  • Barcode: 025192018626
  • Format: DVD
  • Runtime: 121 min.
  • Discs: 1
  • Title: 12 Monkeys
  • Year: 1995

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?

like image 518
Ethan Allen Avatar asked Jul 05 '16 21:07

Ethan Allen


4 Answers

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.

like image 101
Barmar Avatar answered Oct 30 '22 02:10

Barmar


I suggest there is no 'proper' way. But you might like this...

  • Your Movie table stays as is. (I assume there is an id.)
  • Another table, UserMovie with the same columns except:
    • All the columns except id are NULL
    • It has another column: 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.)

like image 45
Rick James Avatar answered Oct 30 '22 02:10

Rick James


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.

like image 41
Darwin von Corax Avatar answered Oct 30 '22 02:10

Darwin von Corax


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

like image 29
SIDU Avatar answered Oct 30 '22 02:10

SIDU