Query SQL per un sistema di catalogazione tramite "Tagging"
Introduzione ai tag
La catalogazione tramite tag, con i suoi pregi, i suoi difetti, i suoi limiti e le sue potenzialità sta molto emergendo con l'avvento del così detto Web 2.0, ed è sempre più spesso il sistema adottato dai blog e dai sistemi di catalogazione folksonomica. La differenza concettuale di una catalogazione a tag rispetto ad una più "tradizionale", è che in questo tipo di catalogazione non ci sono gerarchie, ma relazioni. Ma qual'è la struttura di un database non gerarchico? E come si possono ricavarne relazioni? In questo articolo, dal taglio molto pratico, cercheremo di introdurre questo argomento.
Sommario:
- Le tabelle.
- Tag associati al contenuto.
- Contenuti associati ai tag.
- Tag correlati.
- Contenuti correlati.
Le tabelle
Semplificando, una struttura di tabelle di database per gestire una catalogazione tramite tag potrebbe essere come la seguente. La tabella posts, con i contenuti da catalogare:
| post_id | content |
|---|---|
| 1 | Un post su SQL, tags e PHP. |
| 2 | Un post su come creare una nuvola di tag con PHP e CSS. |
| 3 | Un menu carino con i CSS... |
La tabella tags, con l'elenco dei tag utilizzati:
| tag_id | tag |
|---|---|
| 1 | SQL |
| 2 | PHP |
| 3 | Tags |
| 4 | CSS |
| 5 | Menu |
La tabella posts2tags che mette in relazioni i post con i relativi tag associati:
| rel_id | post_id | tag_id |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 2 | 2 |
| 5 | 2 | 3 |
| 6 | 2 | 4 |
| 7 | 3 | 4 |
| 8 | 3 | 5 |
Tag associati al contenuto
Riferendoci alle tabelle appena viste, la query per ricavare i tag associati al post con post_id uguale ad 1 è:
SELECT tags.tag
FROM posts, tags, posts2tags
WHERE posts2tags.post_id = posts.post_id
AND posts2tags.tag_id = tags.tag_id
AND posts2tags.post_id = 1
ORDER BY tags.tag
Contenuti associati ai tag
Ricavare i contenuti associati ai tag è un po' più complicato, se i tag sono più di uno. Con la seguente query selezioneremo tutti i post associati al tag "SQL" e "tags":
SELECT posts.post_id, posts.content
FROM posts, tags, posts2tags
WHERE posts2tags.post_id = posts.post_id
AND posts2tags.tag_id = tags.tag_id
AND tags.tag IN ('SQL', 'tags')
GROUP BY posts.post_id
HAVING COUNT( posts2tags.post_id ) = 2
Come abbiamo detto, la query seleziona i post associati al tag SQL o a tags (tags.tag IN ('SQL', 'tags')) e, per restingere la selezione ai post associati ad entrambi i tag, anzichè ad uno soltanto, seleziona solo i post che appaiono 2 volte (HAVING COUNT( posts2tags.post_id ) = 2). 2 perché i tag sono 2, se i tag fossero stati tre sarebbe stato HAVING COUNT( posts2tags.post_id ) = 3 e così via...
Tag correlati
Quando si mostrano i post legati ad uno o più tag, è tipico mostrare anche i tag correlati a quelli correnti. Per individuarli, una volta selezionati gli ID dei contenuti (nel nostro caso i post_id) associati ai tag correnti, come descritto nel punto precedente, si avrà una query come questa:
SELECT tags.tag FROM tags, posts2tags WHERE posts2tags.tag_id = tags.tag_id AND posts2tags.post_id IN (1,2) AND tags.tag NOT IN ('SQL','tags')GROUP BY posts2tags.tag_idORDER BY tags.tag
La query seleziona tutti i tag associati ai contenuti correnti (i post con post_id 1 e 2, in questo caso) escludento i tag "SQL" e "tags", dato che, essendo i tag correnti, sarebbe inutile mostrarli come tag correlati. DISTINCT serve per eliminare i "dopioni" ed avere solo l'elenco dei tag diversi tra loro.
Contenuti correlati
Per selezionare i contenuti che hanno più tag in comune con il contenuto corrente si può utilizzare una query come questa:
SELECT posts.*, count(posts2tags.post_id) as num
FROM posts, tags, posts2tags
WHERE posts2tags.post_id = posts.post_id
AND posts2tags.tag_id = tags.tag_id
AND tags.tag IN ('SQL','tags')
AND posts.post_id != 1
GROUP BY posts2tags.post_id
ORDER BY num DESC, content DESC
Con questa query abbiamo ottenuto i post ordinati in base al numero di tag in comune con il post corrente (quello con post_id uguale ad 1, in questo caso).
Ottimizzazione
Per ottimizzare lo schema o vederne di alternativi, potete far riferimento all'articolo sul Wiki MySQL Forge: TagSchema, in particolare alla sezione Recommended Architecture.
