Empfohlenes SQL-Datenbankdesign für Tags oder Tagging [geschlossen]
-
09-06-2019 - |
Frage
Ich habe von einigen Möglichkeiten gehört, Tagging zu implementieren.Verwenden einer Zuordnungstabelle zwischen TagID und ItemID (macht für mich Sinn, aber ist sie skalierbar?), Hinzufügen einer festen Anzahl möglicher TagID-Spalten zu ItemID (scheint eine schlechte Idee zu sein), Behalten von Tags in einer Textspalte, die durch Kommas getrennt ist (klingt verrückt, könnte aber funktionieren).Ich habe sogar gehört, dass jemand eine Sparse-Matrix empfohlen hat, aber wie wachsen die Tag-Namen dann elegant?
Vermisse ich eine Best Practice für Tags?
Lösung
Drei Tabellen (eine zum Speichern aller Elemente, eine für alle Tags und eine für die Beziehung zwischen beiden), ordnungsgemäß indiziert und mit Fremdschlüsseln, die in einer geeigneten Datenbank ausgeführt werden, sollten gut funktionieren und ordnungsgemäß skaliert werden.
Table: Item
Columns: ItemID, Title, Content
Table: Tag
Columns: TagID, Title
Table: ItemTag
Columns: ItemID, TagID
Andere Tipps
Normalerweise würde ich Yaakov Ellis zustimmen, aber in diesem speziellen Fall gibt es eine andere praktikable Lösung:
Verwenden Sie zwei Tabellen:
Table: Item
Columns: ItemID, Title, Content
Indexes: ItemID
Table: Tag
Columns: ItemID, Title
Indexes: ItemId, Title
Dies hat einige große Vorteile:
Erstens macht es die Entwicklung viel einfacher:in der Drei-Tabellen-Lösung zum Einfügen und Aktualisieren von item
Sie müssen nachschlagen Tag
Tabelle, um zu sehen, ob bereits Einträge vorhanden sind.Dann muss man ihnen neue hinzufügen.Das ist keine triviale Aufgabe.
Dann werden Abfragen einfacher (und möglicherweise schneller).Es gibt drei Hauptdatenbankabfragen, die Sie durchführen werden:Alles ausgeben Tags
für eine Item
, zeichnen Sie eine Tag-Wolke und wählen Sie alle Elemente für einen Tag-Titel aus.
Alle Tags für einen Artikel:
3-Tisch:
SELECT Tag.Title
FROM Tag
JOIN ItemTag ON Tag.TagID = ItemTag.TagID
WHERE ItemTag.ItemID = :id
2-Tisch:
SELECT Tag.Title
FROM Tag
WHERE Tag.ItemID = :id
Tag-Cloud:
3-Tisch:
SELECT Tag.Title, count(*)
FROM Tag
JOIN ItemTag ON Tag.TagID = ItemTag.TagID
GROUP BY Tag.Title
2-Tisch:
SELECT Tag.Title, count(*)
FROM Tag
GROUP BY Tag.Title
Artikel für einen Tag:
3-Tisch:
SELECT Item.*
FROM Item
JOIN ItemTag ON Item.ItemID = ItemTag.ItemID
JOIN Tag ON ItemTag.TagID = Tag.TagID
WHERE Tag.Title = :title
2-Tisch:
SELECT Item.*
FROM Item
JOIN Tag ON Item.ItemID = Tag.ItemID
WHERE Tag.Title = :title
Aber es gibt auch einige Nachteile:Es könnte mehr Speicherplatz in der Datenbank beanspruchen (was zu mehr und langsameren Festplattenvorgängen führen könnte) und es ist nicht normalisiert, was zu Inkonsistenzen führen könnte.
Das Größenargument ist nicht so stark, da es in der Natur von Tags liegt, dass sie normalerweise ziemlich klein sind, sodass die Größenzunahme nicht groß ist.Man könnte argumentieren, dass die Abfrage des Tag-Titels in einer kleinen Tabelle, die jedes Tag nur einmal enthält, viel schneller ist, und das trifft sicherlich zu.Wenn man jedoch die Einsparungen bedenkt, die dadurch entstehen, dass man nicht beitreten muss, und die Tatsache, dass man daraus einen guten Index erstellen kann, könnte dies leicht kompensiert werden.Dies hängt natürlich stark von der Größe der verwendeten Datenbank ab.
Auch das Argument der Inkonsistenz ist ein wenig fraglich.Tags sind Freitextfelder und es gibt keinen erwarteten Vorgang wie „Alle Tags „foo“ in „bar“ umbenennen“.
Also tldr:Ich würde mich für die Zwei-Tisch-Lösung entscheiden.(Tatsächlich werde ich es tun.Ich habe diesen Artikel gefunden, um zu sehen, ob es stichhaltige Argumente dagegen gibt.)
Wenn Sie eine Datenbank verwenden, die Map-Reduce unterstützt, wie z. B. Couchdb, ist das Speichern von Tags in einem Nur-Text-Feld oder Listenfeld tatsächlich die beste Möglichkeit.Beispiel:
tagcloud: {
map: function(doc){
for(tag in doc.tags){
emit(doc.tags[tag],1)
}
}
reduce: function(keys,values){
return values.length
}
}
Wenn Sie dies mit „group=true“ ausführen, werden die Ergebnisse nach Tag-Namen gruppiert und sogar die Anzahl der Treffer dieses Tags zurückgegeben.Es ist sehr ähnlich Zählen der Vorkommen eines Wortes im Text.
Verwenden Sie eine einzelne formatierte Textspalte[1] zum Speichern der Tags und verwenden Sie eine leistungsfähige Volltextsuchmaschine, um diese zu indizieren.Andernfalls treten Skalierungsprobleme auf, wenn Sie versuchen, boolesche Abfragen zu implementieren.
Wenn Sie Details zu Ihren Tags benötigen, können Sie diese entweder in einer inkrementell gepflegten Tabelle verfolgen oder einen Batch-Job ausführen, um die Informationen zu extrahieren.
[1] Einige RDBMS stellen sogar einen nativen Array-Typ bereit, der möglicherweise noch besser für die Speicherung geeignet ist, da kein Parsing-Schritt erforderlich ist, aber Probleme bei der Volltextsuche verursachen kann.
Ich habe die Tags immer in einer separaten Tabelle gespeichert und hatte dann eine Zuordnungstabelle.Natürlich habe ich auch noch nie etwas wirklich Großes gemacht.
Mit einer „Tags“-Tabelle und einer Kartentabelle ist es ziemlich einfach, Tag-Wolken usw. zu generieren, da Sie ganz einfach SQL zusammenstellen können, um eine Liste von Tags mit der Häufigkeit der Verwendung jedes Tags zu erhalten.
Ich würde folgendes Design vorschlagen:Artikeltabelle:Artikel-ID, Taglist1, Taglist2
Dies geht schnell und erleichtert das Speichern und Abrufen der Daten auf Artikelebene.
Erstellen Sie parallel eine weitere Tabelle:Tags Tags erstellen Sie keine eindeutige Bezeichnerin und wenn Ihnen in der 2. Spalte der Platz ausgeht, in dem wir 100 Elemente erstellen, erstellen Sie eine andere Zeile.
Jetzt geht die Suche nach Artikeln für ein Tag superschnell.