Acredito que você achará interessante esta postagem do blog: Tags: Esquemas de banco de dados
O problema: você deseja ter um esquema de banco de dados em que possa marcar um favorito (ou uma postagem de blog ou qualquer outra coisa) com quantas marcas desejar. Posteriormente, você deseja executar consultas para restringir os marcadores a uma união ou interseção de tags. Você também deseja excluir (digamos: menos) algumas tags do resultado da pesquisa.
Solução “MySQLicious”
Nesta solução, o esquema possui apenas uma tabela, está desnormalizado. Este tipo é chamado de “solução MySQLicious” porque MySQLicious importa dados del.icio.us em uma tabela com esta estrutura.
Consulta de interseção (AND) para “search + webservice + semweb”:
SELECT *
FROM `delicious`
WHERE tags LIKE "%search%"
AND tags LIKE "%webservice%"
AND tags LIKE "%semweb%"
Consulta de união (OR) para “search | webservice | semweb”:
SELECT *
FROM `delicious`
WHERE tags LIKE "%search%"
OR tags LIKE "%webservice%"
OR tags LIKE "%semweb%"
Menos consulta para “search + webservice-semweb”
SELECT *
FROM `delicious`
WHERE tags LIKE "%search%"
AND tags LIKE "%webservice%"
AND tags NOT LIKE "%semweb%"
Solução “Scuttle”
Scuttle organiza seus dados em duas tabelas. Essa tabela “scCategories” é a tabela “tag” e tem uma chave estrangeira para a tabela “bookmark”.
Consulta de interseção (AND) para “bookmark + webservice + semweb”:
SELECT b.*
FROM scBookmarks b, scCategories c
WHERE c.bId = b.bId
AND (c.category IN ('bookmark', 'webservice', 'semweb'))
GROUP BY b.bId
HAVING COUNT( b.bId )=3
Primeiro, todas as combinações de bookmark-tag são pesquisadas, onde a tag é “bookmark”, “webservice” ou “semweb” (c.category IN ('bookmark', 'webservice', 'semweb')), então apenas os favoritos que todas as três marcas pesquisadas são levadas em consideração (HAVING COUNT (b.bId) = 3).
Consulta de união (OR) para “bookmark | webservice | semweb”:
Basta omitir a cláusula HAVING e você terá união:
SELECT b.*
FROM scBookmarks b, scCategories c
WHERE c.bId = b.bId
AND (c.category IN ('bookmark', 'webservice', 'semweb'))
GROUP BY b.bId
Menos (Exclusão) Consulta para “bookmark + webservice-semweb”, ou seja: bookmark AND webservice AND NOT semweb.
SELECT b. *
FROM scBookmarks b, scCategories c
WHERE b.bId = c.bId
AND (c.category IN ('bookmark', 'webservice'))
AND b.bId NOT
IN (SELECT b.bId FROM scBookmarks b, scCategories c WHERE b.bId = c.bId AND c.category = 'semweb')
GROUP BY b.bId
HAVING COUNT( b.bId ) =2
Deixar de fora HAVING COUNT leva à consulta de “bookmark | webservice-semweb”.
Solução “Toxi”
A Toxi criou uma estrutura de três mesas. Por meio da tabela “mapa de tags”, os favoritos e as tags são relacionados de n para m. Cada tag pode ser usada junto com diferentes marcadores e vice-versa. Este esquema de banco de dados também é usado pelo wordpress. As consultas são praticamente as mesmas da solução “scuttle”.
Consulta de interseção (AND) para “bookmark + webservice + semweb”
SELECT b.*
FROM tagmap bt, bookmark b, tag t
WHERE bt.tag_id = t.tag_id
AND (t.name IN ('bookmark', 'webservice', 'semweb'))
AND b.id = bt.bookmark_id
GROUP BY b.id
HAVING COUNT( b.id )=3
Consulta de União (OR) para “bookmark | webservice | semweb”
SELECT b.*
FROM tagmap bt, bookmark b, tag t
WHERE bt.tag_id = t.tag_id
AND (t.name IN ('bookmark', 'webservice', 'semweb'))
AND b.id = bt.bookmark_id
GROUP BY b.id
Menos (Exclusão) Consulta para “bookmark + webservice-semweb”, ou seja: bookmark AND webservice AND NOT semweb.
SELECT b. *
FROM bookmark b, tagmap bt, tag t
WHERE b.id = bt.bookmark_id
AND bt.tag_id = t.tag_id
AND (t.name IN ('Programming', 'Algorithms'))
AND b.id NOT IN (SELECT b.id FROM bookmark b, tagmap bt, tag t WHERE b.id = bt.bookmark_id AND bt.tag_id = t.tag_id AND t.name = 'Python')
GROUP BY b.id
HAVING COUNT( b.id ) =2
Deixar de fora HAVING COUNT leva à consulta de “bookmark | webservice-semweb”.