✏️ Explanatory Question
Level: Hard — Tests schema-design judgment; EAV is seductive but a notorious performance trap.
EAV stores each attribute as a row instead of a column. Rather than a wide table with fixed columns, you have three parts: the Entity (which product), the Attribute (which property), and the Value. This lets you add attributes without schema changes.
CREATE TABLE products (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
type VARCHAR(50)
);
-- The EAV table: one row per (product, attribute)
CREATE TABLE product_attributes (
product_id BIGINT NOT NULL,
attribute_name VARCHAR(100) NOT NULL,
attribute_value VARCHAR(500),
PRIMARY KEY (product_id, attribute_name),
INDEX idx_attr (attribute_name, attribute_value)
);
| product_id | attribute_name | attribute_value |
|---|---|---|
| 1 | RAM | 16GB |
| 1 | CPU | Intel i7 |
| 1 | screen | 15.6 inch |
| 2 | size | Large |
| 2 | color | Blue |
-- "Find products with RAM = 16GB AND CPU = Intel i7"
-- Needs ONE self-join PER attribute condition -> ugly and slow
SELECT p.id, p.name
FROM products p
JOIN product_attributes a1
ON p.id = a1.product_id AND a1.attribute_name = 'RAM' AND a1.attribute_value = '16GB'
JOIN product_attributes a2
ON p.id = a2.product_id AND a2.attribute_name = 'CPU' AND a2.attribute_value = 'Intel i7';
-- 5 filter attributes = 5 joins. This does not scale.
-- Store flexible attributes in a single JSON column (MySQL 5.7+)
CREATE TABLE products_json (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
type VARCHAR(50),
attributes JSON
);
INSERT INTO products_json (name, type, attributes) VALUES
('Dell XPS', 'laptop', '{"RAM":"16GB","CPU":"Intel i7","screen":"15.6 inch"}'),
('Cool Tee', 'shirt', '{"size":"Large","color":"Blue"}');
-- Query JSON attributes directly — no joins!
SELECT id, name
FROM products_json
WHERE attributes->>'$.RAM' = '16GB'
AND attributes->>'$.CPU' = 'Intel i7';
-- Index a hot JSON path with a generated column for speed
ALTER TABLE products_json
ADD COLUMN ram VARCHAR(20) AS (attributes->>'$.RAM') STORED,
ADD INDEX idx_ram (ram);
| Approach | Best When | Weakness |
|---|---|---|
| Wide table (fixed columns) | Stable, known attributes | Can't handle dynamic attrs |
| JSON column | Flexible attrs, modern MySQL | Weaker constraints than columns |
| EAV | Truly unlimited/unknown attrs, rare | Join hell, no types, slow |