简体   繁体   中英

MySQL database design for image options relationships

I have two tables, images and image_data and here is an example of my image_data table.

image_id | slide_id | language_id  | type   |
101      | 1        | 1            | CQ     |
101      | 2        | NULL         | NULL   |
56       | 5        | 1            | TN     |
56       | NULL     | 2            | NULL   |

So basically, each image will have different options and I am wondering the best way to implement this.. because I have a feeling I am doing this the wrong way.

With this, I can run a query to use GROUP_CONCAT() to turn values in multiple rows into a single concatenated string.

image_id | slide_id | language_id  | type   |
101      | 1,2      | 1            | CQ     |
56       | 5        | 1,2          | TN     |

Which is fine, but the problem with the way I am doing it right now is..it seems like it will be really difficult to update the rows with my backend system.

在此输入图像描述

So with my query, I can determine which ones to check based on the database since I have it all in one row since I concatenated it. But now it's like.. when I go to click "Save" and update the rows, which one do I update? there can be more than 1 row of the same image id, so how would I update the right one, and so on.

If I checked off another slide for image #101 then I would need to create a new row for it. If after that I wanted to add another language_id to it, then I would need to make sure to not add a new row since one exists with a NULL value, and to just replace the NULL value with the new language id.

It just seems really complicated and there's so many factors, that using this method is really hard to program.

What would be the best way to do this? Any suggestions are really appreciated.

Thanks!

What you need to do is implement N:M (many-to-many) relationships between your images and slides / languages / types tables so that your design is more normalized (one fact in one place).

Think of it this way: one image can have multiple slides , and one slide may be an option of multiple images . -- this is a N:M relationship. Same goes for languages and types.

What you need to do is get rid of your image_data table which houses the options between ALL entities and have three separate cross-reference tables instead. Here's how you would model it:


Base tables:

images (image_id [PK], ...)

slides (slide_id [PK], slide_name, ...)

languages (language_id [PK], language_name, ...)

types (type_name [PK], ...)

Cross-Reference tables:

images_has_slides (image_id [PK], slide_id [PK])

images_has_languages (image_id [PK], language_id [PK])

images_has_types (image_id [PK], type_name [PK])

How it would look in ER:

多对多ER图

With this type of design, you wouldn't have to deal with NULL values or figuring out which row to update because you now have just one fact in one place . To get all options, you would still have to do GROUP_CONCAT() like so:

SELECT
    a.*,
    GROUP_CONCAT(c.slide_name) AS slides,
    GROUP_CONCAT(e.language_name) AS languages,
    GROUP_CONCAT(f.type_name) AS types
FROM
    images a
LEFT JOIN
    images_has_slides b ON a.image_id = b.image_id
LEFT JOIN
    slides c ON b.slide_id = c.slide_id
LEFT JOIN
    images_has_languages d ON a.image_id = d.image_id
LEFT JOIN
    languages e ON d.language_id = e.language_id
LEFT JOIN
    images_has_types f ON a.image_id = f.image_id
GROUP BY
    a.image_id

Then to update image options, you would use INSERT and DELETE on the cross-reference tables:

Let's say you wanted to add two languages to an image, you would do

INSERT INTO images_has_languages (image_id, language_id) 
VALUES (101, 4), (101, 5);

The above query adds languages with id's of 4 and 5 to the image that has an id of 101 .

To remove options (unchecking on the form) - let's say you wanted to remove 2 slides from an image

DELETE FROM images_has_slides WHERE image_id = 101 AND slide_id IN (3,6)

This removes slides with id's of 3 and 6 from the image that has an id of 101 .

So in your application, you could figure out if you need to do insert/delete queries based on if the user unchecked or checked values in the form for the image.

You need to normalize your schema.

  1. You have images table:
    CREATE TABLE images (
        image_id   integer,
        image_name varchar(100),
        PRIMARY KEY(image_id)
    );
  1. Each image can have several slides :
    CREATE TABLE slides (
        slide_id   integer,
        image_id   integer,
        slide_name varchar(100),
        PRIMARY KEY(slide_id)
    );

The same goes for the image_types and image_languages . I hope you understand the logic. And make sure to add proper FOREIGN KEY constraints. Also, it is a good idea to CREATE INDEX on the image_id columns of the subordinate tables.

Now, you have 1 row per each parameter in the related tables. Managing the contents should be easy: INSERT new records when some features are selected and DELETE them when those are deselected. The query (based on the outlined 2 tables) should be:

SELECT i.image_id, i.image_name,
       group_concat(s.slide_id) AS slides
  FROM images i
  LEFT JOIN slides s USING (image_id)
 GROUP BY i.image_id;

Some notes:

  1. It is safe to do GROUP BY only by image_id , as it is a PRIMARY KEY of the iamges and thus it will guarantee single-row groupping;
  2. If you'd like to have slide_id (also language_id , type_id and others) starting from 1 for each of the images, you might go for a 2-field primary keys in the subordinate table, like PRIMARY KEY (image_id, slide_id) .

EDIT:

A note on the many-to-many relations. If you happen to have 2 sets of related data, like images can have many slides and slide_id can be shared by many image_id , then you need an extra table:

CREATE TABLE images (
    image_id   integer,
    image_name varchar(100),
    PRIMARY KEY(image_id)
);

CREATE TABLE slides (
    slide_id   integer,
    slide_name varchar(100),
    PRIMARY KEY(slide_id)
);

CREATE TABLE image_slides (
    image_id   integer,
    slide_id   integer,
    create_dt  timestamp,
    PRIMRY KEY (image_id, slide_id)
);

Have you tried splitting the tables? If you make a separate table for the slide and language and kept the type in the same table as the image ID you could then use that to make your lists. You could then optimize your database with foreign keys so you don't take as big a performance hit.

Here what I mean:

Image data table: two columns, image_id and image_type (type is a reserved word). Imageid is the primary key so there are no duplicates (assuming you only want one type for each image)

Image-language table: two columns, image id and image_language. Both are primary keys so you don't duplicate languages on the same image id, but an image id can have multiple languages. Primary key from image id links to the primary key in the image data table

Image-slide table: two columns, image id and slide number. Same as above (two primary keys, relationship, etc)

This way you could get get all the data like so:

SELECT d.image_id, d.image_type, l.image_language, s.slide_number FROM image_data d LEFT JOIN image_language l ON d.image_id = l.image_id LEFT JOIN image_slide s ON s.image_id = s.image_id

The left joins make sure all the item id always shows up no matter what even if there isn't enough languages or slides to go around. It will create a "matrix" of sorts for you with a row for each image and each language and each slide it applies to. For example, if you had an image that had spanish and english as its language and 4 slides, you would get 8 entries: one for each slide in each language.

I don't know if that will necessarily solve the problem, but it would make it a little easier to control exactly what is in the database while still having the database do a bit of the work for you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM