Рассмотрим следующее, которое представляет собой один из популярных подходов при работе с EAV:
DROP TABLE IF EXISTS products;
CREATE TABLE products
(product_id SERIAL PRIMARY KEY
,name VARCHAR(20) NOT NULL UNIQUE
);
INSERT INTO products VALUES
(1,'Track Pants'),
(2,'PT tshirt');
DROP TABLE IF EXISTS product_options;
CREATE TABLE product_options
(product_id INT NOT NULL
,attribute VARCHAR(12) NOT NULL
,value VARCHAR(12) NOT NULL
,PRIMARY KEY(product_id,attribute)
);
INSERT INTO product_options VALUES
(1,'size',32),
(1,'color','yellow'),
(2,'size',25);
SELECT p.*
, MAX(CASE WHEN po.attribute = 'color' THEN value END) color
, MAX(CASE WHEN attribute = 'size' THEN value END) size
FROM products p
LEFT
JOIN product_options po
ON po.product_id = p.product_id
GROUP
BY p.product_id;
+------------+-------------+--------+------+
| product_id | name | color | size |
+------------+-------------+--------+------+
| 1 | Track Pants | yellow | 32 |
| 2 | PT tshirt | NULL | 25 |
+------------+-------------+--------+------+
Тем не менее, нет ничего плохого в нескольких LEFT JOIN; на самом деле это может быть немного быстрее:
SELECT p.*
, color.value color
, size.value size
FROM products p
LEFT
JOIN product_options color
ON color.product_id = p.product_id
AND color.attribute = 'color'
LEFT
JOIN product_options size
ON size.product_id = p.product_id
AND size.attribute = 'size';
+------------+-------------+--------+------+
| product_id | name | color | size |
+------------+-------------+--------+------+
| 1 | Track Pants | yellow | 32 |
| 2 | PT tshirt | NULL | 25 |
+------------+-------------+--------+------+
Кроме того, хотя и не всегда популярно, при использовании EAV мне нравится разбивать атрибуты на отдельные таблицы в соответствии с типом данных, поэтому у вас есть таблица целых чисел вещи типа, и таблица вещей типа строки.