Relate 3 tables in mysql with restrictions

0

I have 4 tables, some images, some categories and some attributes for each category. The fact is that I need a fourth table that relates for each image an attribute for a category, but at most one attribute of each category.

Images

ImageID NameImage

Categories

CategoryID CategoryName

Attributes

AttributeID CategoryID NameAttribute

and a last table that relates the images with a category and an attribute of those in that category

ImagesCategory

ImageID CategoryID AttributeID

Each image can only have one attribute of a given category, with which ImageID and CategoryID would be the key.

The doubt I have is that really CategoriaID I would be left over from the table of ImagenesCategoria, since it is implicit in AttributoID.

I have 2 options: -Leave CategoryID, with which you could put an Attribute ID and CategoryID that would not correspond to those in the Attributes table

-Remove Category ID, in this case as I could do to not put in CategoryCategory two Attributes of the same Category

To make it clearer I give an example:

Categories

1 color 2 way

Images

34 triangulo-amarillo.jpg 35 circulo-rojo.jpg

Attributes

1 1 red 2 1 green 3 1 yellow 4 2 square 5 2 circle 6 2 triangle

ImagesCategory

34 1 3 --- > it would be yellow 34 2 6 --- > it would be triangle 35 1 1 ---- > Red 35 2 5 ----

What I want to avoid is to put more than one color to an image or to put something for the color category that is not of the category for example (circle, square)

    
asked by L.M 25.10.2017 в 19:35
source

2 answers

1

Some things are not clear in your design ... My answer is aimed at approaching the problem from another point of view.

I consider that the three elements: imagenes, categorias, atributos can exist separately in two tables.

If it's about assigning categories and attributes to images, then we could implement a fourth table that manages relationships. In each row of that table there will then be a complete descriptive element of each image.

Now ... I have purposely added an image with id 5 , to illustrate that your design could still be perfected ... but without deep knowledge of the context I can not give you more suggestions.

Code

VIEW DEMO

CREATE TABLE IF NOT EXISTS imagenes_20171025 
(
    imagen_id   INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    imagen_nom  VARCHAR(150)

)ENGINE=INNODB;


CREATE TABLE IF NOT EXISTS categorias_20171025 (
    categoria_id  INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    categoria_nom VARCHAR(150) 
)ENGINE=INNODB;  


CREATE TABLE IF NOT EXISTS atributos_20171025 (
    atributo_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    atributo_nom VARCHAR(150) 
)ENGINE=INNODB;

CREATE TABLE IF NOT EXISTS imagenes_categorias_atributos_20171025 (
    imagen_id INT, 
    categoria_id INT, 
    atributo_id INT, 
    UNIQUE KEY (imagen_id, categoria_id, atributo_id) ,
     FOREIGN KEY fk_img (imagen_id)    REFERENCES imagenes_20171025   (imagen_id)    ON DELETE CASCADE,
     FOREIGN KEY fk_cat (categoria_id) REFERENCES categorias_20171025 (categoria_id) ON DELETE CASCADE,
     FOREIGN KEY fk_atr (atributo_id)  REFERENCES atributos_20171025  (atributo_id)  ON DELETE CASCADE
)ENGINE=INNODB;

INSERT INTO imagenes_20171025 (imagen_nom)
    VALUES 
    ('triangulo-amarillo.jpg'),
    ('rombo-azul.jpg'),
    ('rombo-verde-mediano.jpg'),
    ('trapecio-mediano.jpg'),
    ('cuadrado-redondo????.jpg')
;

INSERT INTO categorias_20171025 (categoria_nom)
    VALUES 
    ('Color'),
    ('Forma'),
    ('Tamaño')
;    

INSERT INTO atributos_20171025 (atributo_nom)
    VALUES 
    ('Azul'),
    ('Amarillo'),
    ('Verde'),
    ('Mediano'),
    ('Redondo')
;

INSERT INTO imagenes_categorias_atributos_20171025 (imagen_id, categoria_id, atributo_id)
    VALUES 
    (1,1,2),
    (2,1,1),
    (3,1,3),
    (4,3,4),
    (5,2,5)
;


/*
SELECT * FROM imagenes_20171025;
SELECT * FROM categorias_20171025;
SELECT * FROM atributos_20171025;
SELECT * FROM atributos_20171025;
*/

SELECT 
    ica.imagen_id, ica.categoria_id, ica.atributo_id, 
    i.imagen_nom, c.categoria_nom, a.atributo_nom 
FROM imagenes_categorias_atributos_20171025 ica 
    LEFT JOIN imagenes_20171025 i ON ica.imagen_id=i.imagen_id 
    LEFT JOIN categorias_20171025 c ON ica.categoria_id=c.categoria_id 
    LEFT JOIN atributos_20171025 a ON ica.atributo_id=a.atributo_id 
ORDER BY i.imagen_id;

Result:

imagen_id   categoria_id    atributo_id imagen_nom                  categoria_nom   atributo_nom
1           1               2           triangulo-amarillo.jpg      Color           Amarillo
2           1               1           rombo-azul.jpg              Color           Azul
3           1               3           rombo-verde-mediano.jpg     Color           Verde
4           3               4           trapecio-mediano.jpg        Tamaño          Mediano
5           2               5           cuadrado-redondo????.jpg    Forma           Redondo
    
answered by 26.10.2017 в 00:41
0

Let us agree that the problem can be explained with three postulates:

  • You want to keep a collection of images where each image has an N quantity of attributes whose meaning is free: size, color, alias, weight, width, height, transparency, theme, author (works like putting tags to the image )
  • An image can not have the same attribute twice. (It does not make sense to have two colors or two aliases)
  • Each attribute of a certain image can not have more than one value.

Then we would have a table of images where each image has a unique id.

Table image

CREATE TABLE IF NOT EXISTS imagen (
    id   INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    nombre_imagen  TEXT
);

With this we are saying that each image is unique, but nothing prevents two images from being called the same. If you think of a storage system where users can upload files, surely all users can upload prueba.jpg and they are different files with the same name. If for example a limited collection of avatars where each user can choose his, the model changes. There are many cases of use and it is not relevant to detail the implementation here.

Table attribute (it was called category, I think it's clearer that way)

CREATE TABLE IF NOT EXISTS atributo (
  id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
  nombre_atributo TEXT NOT NULL CONSTRAINT nombre_atributo_unique UNIQUE
);

Each attribute has a unique id. His name is also unique. I think so as to fill a selector in a user interface where you can add attributes to your image from a list, but that list admits adding attributes that do not exist (example: using Select2 with the tags attribute in true ). So, for example, 1 means color and 2 means dimensions . It is a dictionary of unique tuples.

Table image_attribute (this is the table that relates the previous two)

CREATE TABLE IF NOT EXISTS imagen_atributo (
  id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
  imagen_id int NOT NULL,
  atributo_id int NOT NULL,
  atributo_valor TEXT ,
  UNIQUE KEY (imagen_id, atributo_id) ,
  FOREIGN KEY fk_img (imagen_id) REFERENCES imagen (id) ON DELETE CASCADE,
  FOREIGN KEY fk_atributo (atributo_id) REFERENCES atributo (id) ON DELETE CASCADE
);

This is the table that relates images, attributes and attribute values. The attribute_map table has a unique id that means nothing. Perfectly we could choose (imagen_id, atributo_id) as primary key, but combined primary keys (and foreign keys combined) in my experience is looking for problems. Anyway, the tuple (image_id, attribute_id) is unique. Sure, if an image is deleted all its attributes are deleted (but not deleted from the attribute dictionary). If an attribute is deleted from the dictionary, all the values that each image had in that attribute are deleted (ie all the images lose their 'color' attribute).

We fill in the tables:

INSERT INTO imagen (nombre_imagen)
    VALUES 
    ('casita.jpg'), -- id = 1
    ('perro.jpg'),  -- id = 2
    ('paisaje.jpg') -- id = 3
;

INSERT INTO atributo (nombre_atributo)
    VALUES 
    ('color'),       -- id=1
    ('dimensiones'), -- id=2
    ('peso')         -- id=3
;

INSERT INTO imagen_atributo (imagen_id, atributo_id, valor_atributo)
    VALUES 
    (1,1,'rojo'), -- id=1
    (2,1,'azul'), -- id=2
    (3,2,'40x40'),-- id=3
    (2,3,'40Kb')  -- id=4
;    

Then, to list each attribute of each image:

SELECT ia.imagen_id,
       nombre_imagen,
       ia.atributo_id
       nombre_atributo,
       valor_atributo
FROM imagen_atributo ia
     JOIN imagen ON ia.imagen_id = imagen.id
     JOIN atributo ON ia.atributo_id = atributo.id
ORDER BY ia.id

He would give us

imagen_id  nombre_imagen atributo_id nombre_atributo valor_atributo
1          casita.jpg    1           color           rojo
2          perro.jpg     1           color           azul
3          paisaje.jpg   2           dimensiones     40x40
2          perro.jpg     3           peso            40Kb

This model is not strictly third normal form, because the relation table also has a value, but it is the simplest way that occurs to me to comply with the three postulates of the beginning.

    
answered by 26.10.2017 в 12:21