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.