O entity-attribute-value modelo que você sugere pode se encaixar neste cenário.
Em relação à consulta de filtragem, você deve entender que, com o modelo EAV, você sacrificará muito poder de consulta, então isso pode se tornar bastante complicado. No entanto, esta é uma maneira de resolver seu problema:
SELECT stuff.id
FROM stuff
JOIN (SELECT COUNT(*) matches
FROM table
WHERE (`key` = X1 AND `value` = V1) OR
(`key` = X2 AND `value` = V2)
GROUP BY id
) sub_t ON (sub_t.matches = 2 AND sub_t.id = stuff.id)
GROUP BY stuff.id;
Um recurso deselegante dessa abordagem é que você precisa especificar o número de pares atributo/valor que espera corresponder em
sub_t.matches = 2
. Se tivéssemos três condições, teríamos que especificar sub_t.matches = 3
, e assim por diante. Vamos construir um caso de teste:
CREATE TABLE stuff (`id` varchar(20), `key` varchar(20), `value` varchar(20));
INSERT INTO stuff VALUES ('apple', 'color', 'red');
INSERT INTO stuff VALUES ('mango', 'color', 'yellow');
INSERT INTO stuff VALUES ('banana', 'color', 'yellow');
INSERT INTO stuff VALUES ('apple', 'taste', 'sweet');
INSERT INTO stuff VALUES ('mango', 'taste', 'sweet');
INSERT INTO stuff VALUES ('banana', 'taste', 'bitter-sweet');
INSERT INTO stuff VALUES ('apple', 'origin', 'US');
INSERT INTO stuff VALUES ('mango', 'origin', 'MEXICO');
INSERT INTO stuff VALUES ('banana', 'origin', 'US');
Inquerir:
SELECT stuff.id
FROM stuff
JOIN (SELECT COUNT(*) matches, id
FROM stuff
WHERE (`key` = 'color' AND `value` = 'yellow') OR
(`key` = 'taste' AND `value` = 'sweet')
GROUP BY id
) sub_t ON (sub_t.matches = 2 AND sub_t.id = stuff.id)
GROUP BY stuff.id;
Resultado:
+-------+
| id |
+-------+
| mango |
+-------+
1 row in set (0.02 sec)
Agora vamos inserir outra fruta com
color=yellow
e taste=sweet
:INSERT INTO stuff VALUES ('pear', 'color', 'yellow');
INSERT INTO stuff VALUES ('pear', 'taste', 'sweet');
INSERT INTO stuff VALUES ('pear', 'origin', 'somewhere');
A mesma consulta retornaria:
+-------+
| id |
+-------+
| mango |
| pear |
+-------+
2 rows in set (0.00 sec)
Se quisermos restringir este resultado a entidades com
origin=MEXICO
, teríamos que adicionar outro OR
condição e verifique se há sub_t.matches = 3
em vez de 2
. SELECT stuff.id
FROM stuff
JOIN (SELECT COUNT(*) matches, id
FROM stuff
WHERE (`key` = 'color' AND `value` = 'yellow') OR
(`key` = 'taste' AND `value` = 'sweet') OR
(`key` = 'origin' AND `value` = 'MEXICO')
GROUP BY id
) sub_t ON (sub_t.matches = 3 AND sub_t.id = stuff.id)
GROUP BY stuff.id;
Resultado:
+-------+
| id |
+-------+
| mango |
+-------+
1 row in set (0.00 sec)
Como em toda abordagem, existem certas vantagens e desvantagens ao usar o modelo EAV. Certifique-se de pesquisar o tópico extensivamente no contexto de sua aplicação. Você pode até considerar um banco de dados relacional alternativo, como Cassandra , CouchDB , MongoDB , Voldemort , HBase , SimpleDB ou outros armazenamentos de valores-chave.