Progettazione del database SQL consigliata per tag o tag [chiuso]


288

Ho sentito parlare di alcuni modi per implementare la codifica; usando una tabella di mappatura tra TagID e ItemID (per me ha senso, ma si adatta?), aggiungendo un numero fisso di possibili colonne TagID a ItemID (sembra una cattiva idea), Mantenere i tag in una colonna di testo che è separato da virgola (suoni pazzo ma potrebbe funzionare). Ho persino sentito qualcuno raccomandare una matrice sparsa, ma come fanno i nomi dei tag a crescere con grazia?

Mi sto perdendo una best practice per i tag?


9
Ok, questa è la domanda # 20856, la (quasi) stessa domanda è la # 48475 posta almeno due settimane dopo che questa domanda è stata posta.
dlamblin,

9
Un'altra domanda interessante è "In che modo SO implementa i tag?"
Mostafa,

1
Un'altra domanda interessante è "Li vorresti internazionalizzare, e se sì, come?"
DanMan,

1
Confronto interessante (specifico di Postgres): databasesoup.com/2015/01/tag-all-things.html
a_horse_with_no_name

Risposte:


406

Tre tabelle (una per la memorizzazione di tutti gli elementi, una per tutti i tag e una per la relazione tra i due), opportunamente indicizzate, con chiavi esterne impostate in esecuzione su un database adeguato, dovrebbero funzionare bene e adattarsi correttamente.

Table: Item
Columns: ItemID, Title, Content

Table: Tag
Columns: TagID, Title

Table: ItemTag
Columns: ItemID, TagID

32
Questa è conosciuta come la soluzione "Toxi", puoi trovare ulteriori informazioni al riguardo qui: howto.philippkeller.com/2005/04/24/Tags-Database-schemas
The Pixel Developer

16
Una cosa non mostrata qui sono i "tag" gerarchici o le categorie nella tabella Tag. Ciò è comunemente necessario sui siti che hanno categorie e sottocategorie ma che richiedono la flessibilità della codifica. Ad esempio, siti di ricette, siti di ricambi auto, directory aziendali, ecc. Questi tipi di dati di solito non rientrano in una sola categoria, quindi la codifica è la risposta ma è necessario utilizzare qualcosa come il modello di set nidificato o il modello di elenco di adiacenza nella tabella dei tag.
HK1,

5
Sono d'accordo con HK1 è possibile con la struttura sopra + Tabella: Colonne TagGroup: TagGropuId, Tabella dei titoli: Colonne Tag: TagID, Titolo, TagGroupId
Tuono

quando voglio aggiungere la colonna CSS alla tabella, aggiungerò la colonna CSS nella tabella dei tag?
Amitābha,

10
@ftvs: link nuovamente interrotto, il nuovo link è howto.philippkeller.com/2005/04/24/Tags-Database-schemas
hansaplast

83

Normalmente concordo con Yaakov Ellis ma in questo caso speciale esiste un'altra soluzione praticabile:

Usa due tabelle:

Table: Item
Columns: ItemID, Title, Content
Indexes: ItemID

Table: Tag
Columns: ItemID, Title
Indexes: ItemId, Title

Questo ha alcuni importanti vantaggi:

Innanzitutto rende lo sviluppo molto più semplice: nella soluzione a tre tabelle per l'inserimento e l'aggiornamento di itemte devi cercare la Tagtabella per vedere se ci sono già voci. Quindi devi unirti a loro con nuovi. Questo non è un compito banale.

Quindi rende le query più semplici (e forse più veloci). Esistono tre query di database principali che verranno eseguite: Output tutto Tagsper uno Item, disegnare un Tag-Cloud e selezionare tutti gli elementi per un titolo Tag.

Tutti i tag per un oggetto:

3-Table:

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-Table:

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-Table:

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 (che potrebbe portare a più operazioni del disco che è più lento) e non è normalizzato che potrebbe portare a incongruenze.

L'argomento size non è così forte perché la natura stessa dei tag è che sono normalmente 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 ogni tag una sola volta e questo è certamente vero. Ma considerare i risparmi per non dover aderire e il fatto che è possibile costruire un buon indice su di essi potrebbe facilmente compensare questo. Questo ovviamente dipende fortemente dalle dimensioni del database che si sta utilizzando.

Anche l'argomento dell'incoerenza è un po 'controverso. I tag sono campi di testo gratuiti e non è prevista alcuna operazione come "rinomina tutti i tag" pippo "in" barra "".

Quindi tldr: sceglierei la soluzione a due tavoli. (In effetti lo farò. Ho trovato questo articolo per vedere se ci sono argomenti validi contro di esso.)


"Index: ItemId, Title" indica un indice per ciascuno o un indice contenente entrambi?
DanMan,

Normalmente due indici. Potrebbe dipendere dal database che stai utilizzando, però.
Scheintod

1
Nella tabella dei tag è ItemId e Tag una chiave composita? o hai anche un PK?
Rippo

2
in questo modo non è possibile creare tag "non utilizzati", pertanto è necessario eseguire una funzione "Aggiungi tag" su un elemento. Sull'altro metodo, la funzione "aggiungi tag" può essere eseguita in modo indipendente
Gianluca Ghettini,

1
@Quilang. Credo ancora che dipenda da cosa stai facendo :) L'ho implementato in entrambi i modi in diversi progetti. Nel mio ultimo ho finito con una soluzione a 3 tabelle perché avevo bisogno di un "tipo di tag" (o di qualche altra meta informazione sul tag) e potevo riutilizzare un po 'di codice da un cugino stretto di tag: parametri. Ma nello stesso progetto ho usato esattamente questo metodo per un cugino ancora più vicino: bandiere (es. "Venduto", "nuovo", "caldo")
Scheintod

38

Se stai usando un database che supporta la riduzione della mappa, come couchdb, archiviare i tag in un campo di testo normale 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
  }
}

L'esecuzione con group = true raggrupperà i risultati in base al nome del tag e restituirà anche un conteggio del numero di volte in cui il tag è stato rilevato. È molto simile al conteggio delle occorrenze di una parola nel testo .


4
+1 Bello vedere anche alcune implementazioni NoSQL.
Xeoncross,

@NickRetallack Il collegamento non funziona. Se puoi, aggiorna questa risposta.
xralf,

Ok ho sostituito il link con uno in archive.org
Nick Retallack,

13

Utilizzare una singola colonna di testo formattata [1] per memorizzare i tag e utilizzare un motore di ricerca full-text capace per indicizzarlo. Altrimenti si verificheranno problemi di ridimensionamento quando si tenta di implementare query booleane.

Se sono necessari dettagli sui tag disponibili, è possibile tenerne traccia in una tabella gestita in modo incrementale oppure eseguire un processo batch per estrarre le informazioni.

[1] Alcuni RDBMS forniscono persino un tipo di array nativo che potrebbe essere ancora più adatto per l'archiviazione non necessitando di una fase di analisi, ma potrebbe causare problemi con la ricerca full-text.


Sei a conoscenza di un motore di ricerca full-text che non trova variazioni su una parola? Ad esempio, la ricerca di libri restituisce libri? Inoltre, cosa fai con tag come "c ++"? SQL Server, ad esempio, eliminerebbe i segni più nell'indice. Grazie.
Jonathan Wood,

Prova Sphinx - sphinxsearch.com
Roman

Questo tutorial in 3 parti potrebbe essere utile per coloro che stanno percorrendo questa strada (ricerca di testo completo). Sta usando le strutture native di PostgreSQL: shisaa.jp/postset/postgresql-full-text-search-part-1.html
Sarà

è migliore della risposta selezionata in termini di prestazioni?

che ne dite di archiviare usando varchar 255, i tag separati da virgola e l'aggiunta di un indice di testo kfull su di esso?

9

Ho sempre tenuto i tag in una tabella separata e poi ho avuto una tabella di mapping. Ovviamente non ho mai fatto nulla su larga scala.

Avere una tabella "tag" e una tabella della mappa rende abbastanza banale generare nuvole di tag e simili poiché puoi facilmente mettere insieme SQL per ottenere un elenco di tag con conteggi della frequenza con cui ogni tag viene utilizzato.


6
Ciò è ancora più semplice se non si utilizza una tabella di mapping :)
Scheintod

0

Vorrei suggerire il seguente design: Tabella degli articoli: Itemid, taglist1, taglist2
questo sarà veloce e faciliterà il salvataggio e il recupero dei dati a livello di articolo.

In parallelo, costruisci un'altra tabella: i tag tag non rendono l'identificatore univoco dei tag e se esaurisci lo spazio nella seconda colonna che contiene, diciamo, 100 elementi creano un'altra riga.

Ora, durante la ricerca di articoli per un tag, sarà super veloce.


it.wikipedia.org/wiki/First_normal_form anche se ci sono eccezioni a questo, puoi denormalizzare, ma non qui
Dheeraj
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.