Progettazione del database SQL consigliata per tag o tagging [chiuso]
-
09-06-2019 - |
Domanda
Ho sentito parlare di alcuni modi per implementare il tagging;utilizzando una tabella di mappatura tra TagID e ItemID (ha senso per me, ma è scalabile?), aggiungendo un numero fisso di possibili colonne TagID a ItemID (sembra una cattiva idea), mantenendo i tag in una colonna di testo separata da virgole (sembra pazzesco ma potrebbe funzionare).Ho persino sentito qualcuno consigliare una matrice sparsa, ma come fanno i nomi dei tag a crescere con grazia?
Mi manca una best practice per i tag?
Soluzione
Tre tabelle (una per archiviare tutti gli elementi, una per tutti i tag e una per la relazione tra i due), adeguatamente indicizzate, con chiavi esterne impostate in esecuzione su un database appropriato, dovrebbero funzionare bene e adattarsi correttamente.
Table: Item
Columns: ItemID, Title, Content
Table: Tag
Columns: TagID, Title
Table: ItemTag
Columns: ItemID, TagID
Altri suggerimenti
Normalmente sarei d'accordo con Yaakov Ellis ma in questo caso speciale c'è un'altra soluzione praticabile:
Utilizza due tabelle:
Table: Item
Columns: ItemID, Title, Content
Indexes: ItemID
Table: Tag
Columns: ItemID, Title
Indexes: ItemId, Title
Ciò presenta alcuni vantaggi importanti:
Innanzitutto rende lo sviluppo molto più semplice:nella soluzione a tre tabelle per l'inserimento e l'aggiornamento del item
devi cercare il Tag
tabella per vedere se sono già presenti voci.Quindi devi unirti a loro con quelli nuovi.Questo non è un compito banale.
Quindi rende le query più semplici (e forse più veloci).Ci sono tre principali query sul database che farai:Emetti tutto Tags
per uno Item
, disegna una nuvola di tag e seleziona tutti gli elementi per un titolo di tag.
Tutti i tag per un articolo:
3-Tabella:
SELECT Tag.Title
FROM Tag
JOIN ItemTag ON Tag.TagID = ItemTag.TagID
WHERE ItemTag.ItemID = :id
2-Tabella:
SELECT Tag.Title
FROM Tag
WHERE Tag.ItemID = :id
Tag Cloud:
3-Tabella:
SELECT Tag.Title, count(*)
FROM Tag
JOIN ItemTag ON Tag.TagID = ItemTag.TagID
GROUP BY Tag.Title
2-Tabella:
SELECT Tag.Title, count(*)
FROM Tag
GROUP BY Tag.Title
Articoli per un tag:
3-Tabella:
SELECT Item.*
FROM Item
JOIN ItemTag ON Item.ItemID = ItemTag.ItemID
JOIN Tag ON ItemTag.TagID = Tag.TagID
WHERE Tag.Title = :title
2-Tabella:
SELECT Item.*
FROM Item
JOIN Tag ON Item.ItemID = Tag.ItemID
WHERE Tag.Title = :title
Ma ci sono anche alcuni inconvenienti:Potrebbe richiedere più spazio nel database (il che potrebbe portare a più operazioni sul disco, il che è più lento) e non è normalizzato, il che potrebbe portare a incoerenze.
L'argomento relativo alle dimensioni non è così forte perché la natura stessa dei tag è che normalmente sono piuttosto piccoli, quindi l'aumento delle dimensioni non è grande.Si potrebbe sostenere che la query per il titolo del tag è molto più veloce in una piccola tabella che contiene ciascun tag solo una volta e questo è certamente vero.Ma tenendo conto dei risparmi derivanti dal non dover aderire e del fatto che è possibile creare un buon indice su di essi, si potrebbe facilmente compensare questo problema.Questo ovviamente dipende molto dalla dimensione del database che stai utilizzando.
Anche l’argomento dell’incoerenza è un po’ controverso.I tag sono campi di testo liberi e non è prevista alcuna operazione come "rinominare tutti i tag da "foo" a "bar"".
Quindi tldr:Io opterei per la soluzione a due tavoli.(Infatti lo farò.Ho trovato questo articolo per vedere se ci sono argomenti validi contro di esso.)
Se stai utilizzando un database che supporta la riduzione delle mappe, come couchdb, memorizzare i tag in un campo di testo semplice o in un campo elenco è davvero il modo migliore.Esempio:
tagcloud: {
map: function(doc){
for(tag in doc.tags){
emit(doc.tags[tag],1)
}
}
reduce: function(keys,values){
return values.length
}
}
Eseguendo questo con group=true raggrupperà i risultati per nome del tag e restituirà anche un conteggio del numero di volte in cui il tag è stato incontrato.È molto simile a contare le occorrenze di una parola nel testo.
Utilizza una singola colonna di testo formattato[1] per memorizzare i tag e utilizza un motore di ricerca full-text capace per indicizzarla.Altrimenti ti imbatterai in problemi di ridimensionamento quando provi a implementare query booleane.
Se hai bisogno di dettagli sui tag che hai, puoi tenerne traccia in una tabella mantenuta in modo incrementale o eseguire un lavoro batch per estrarre le informazioni.
[1] Alcuni RDBMS forniscono anche un tipo di array nativo che potrebbe essere ancora più adatto per l'archiviazione poiché non necessita di una fase di analisi, ma potrebbe causare problemi con la ricerca del testo completo.
Ho sempre mantenuto i tag in una tabella separata e poi ho avuto una tabella di mappatura.Naturalmente non ho mai fatto nulla su larga scala.
Avere una tabella "tag" e una tabella della mappa rende piuttosto banale generare tag cloud e simili poiché puoi facilmente mettere insieme SQL per ottenere un elenco di tag con il conteggio della frequenza con cui viene utilizzato ciascun tag.
Suggerirei il seguente design:Tabella degli articoli:ID articolo, taglista1, taglista2
questo sarà veloce e faciliterà il salvataggio e il recupero dei dati a livello di articolo.
In parallelo costruisci un'altra tabella:Tag tag non rendere l'identificatore univoco di tag e se si esaurisce lo spazio in 2a colonna che contiene, diciamo 100 elementi Creare un'altra riga.
Ora la ricerca degli elementi per un tag sarà super veloce.