Zalecany projekt bazy danych SQL dla tagów lub tagowania [zamknięte]

288

Słyszałem o kilku sposobach implementacji tagowania; używając tabeli odwzorowań między TagID a ItemID (ma to dla mnie sens, ale czy skaluje się?), dodając stałą liczbę możliwych kolumn TagID do ItemID (wydaje się to zły pomysł), Utrzymując znaczniki w kolumnie tekstowej oddzielonej przecinkami (dźwięki szalony, ale może działać). Słyszałem nawet, że ktoś poleca rzadką matrycę, ale w jaki sposób nazwy tagów rosną z wdziękiem?

Czy brakuje mi najlepszej praktyki dotyczącej tagów?

dlamblin
źródło
9
Okej, to jest pytanie nr 20856, (prawie) to samo pytanie nr 48475 zostało zadane co najmniej dwa tygodnie po zadaniu tego pytania.
dlamblin,
9
Innym interesującym pytaniem jest „Jak SO implementuje tagi?”
Mostafa
1
Kolejne interesujące pytanie brzmi: „Czy chcesz je umiędzynarodowić, a jeśli tak, to w jaki sposób?”
DanMan
1
Interesujące porównanie (specyficzne dla Postgres): databasesoup.com/2015/01/tag-all-things.html
a_horse_w_na_nazwie

Odpowiedzi:

406

Trzy tabele (jedna do przechowywania wszystkich elementów, jedna do wszystkich tagów i jedna do relacji między nimi), odpowiednio zindeksowane, z zestawem kluczy obcych działającym w odpowiedniej bazie danych, powinny działać dobrze i poprawnie skalować.

Table: Item
Columns: ItemID, Title, Content

Table: Tag
Columns: TagID, Title

Table: ItemTag
Columns: ItemID, TagID
Yaakov Ellis
źródło
32
Jest to znane jako rozwiązanie „Toxi”, dodatkowe informacje na ten temat można znaleźć tutaj: howto.philippkeller.com/2005/04/24/Tags-Database-schemas
The Pixel Developer
16
Jedną z rzeczy, których nie pokazano, są hierarchiczne „tagi” lub kategorie w tabeli tagów. Jest to zwykle potrzebne w witrynach, które mają kategorie i podkategorie, ale wymagają elastyczności tagowania. Na przykład witryny z przepisami, witryny z częściami samochodowymi, katalogi biznesowe itp. Tego rodzaju dane zwykle nie pasują do jednej kategorii, więc tagowanie jest odpowiedzią, ale należy użyć czegoś takiego jak Model zestawu zagnieżdżonego lub Model listy ograniczeń w twojej tabeli tagów.
HK1
5
Zgadzam się z HK1, czy jest to możliwe z powyższą strukturą + Tabela: Kolumny TagGroup: TagGropuId, Tabela tytułów: Kolumny Tag: TagID, Tytuł, TagGroupId
Thunder
kiedy chcę dodać kolumnę css do tabeli, dodam kolumnę css do tabeli tagów?
Amitābha,
10
@ftvs: link ponownie zepsuty, nowy link to howto.philippkeller.com/2005/04/24/Tags-Database-schemas
hansaplast
83

Zwykle zgodziłbym się z Yaakovem Ellisem, ale w tym szczególnym przypadku istnieje inne realne rozwiązanie:

Użyj dwóch tabel:

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

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

Ma to kilka głównych zalet:

Po pierwsze, programowanie jest znacznie prostsze: w rozwiązaniu z trzema tabelami do wstawiania i aktualizacji itemmusisz sprawdzić Tagtabelę, aby sprawdzić, czy są już wpisy. Następnie musisz dołączyć do nich z nowymi. To nie jest trywialne zadanie.

Dzięki temu zapytania stają się prostsze (a być może szybsze). Istnieją trzy główne zapytania do bazy danych, które należy wykonać: Wyprowadź wszystko Tagsdla jednego Item, narysuj chmurkę tagów i wybierz wszystkie elementy dla jednego tytułu tagu.

Wszystkie tagi dla jednej pozycji:

3-stół:

SELECT Tag.Title 
  FROM Tag 
  JOIN ItemTag ON Tag.TagID = ItemTag.TagID
 WHERE ItemTag.ItemID = :id

2-stół:

SELECT Tag.Title
FROM Tag
WHERE Tag.ItemID = :id

Tag-Cloud:

3-stół:

SELECT Tag.Title, count(*)
  FROM Tag
  JOIN ItemTag ON Tag.TagID = ItemTag.TagID
 GROUP BY Tag.Title

2-stół:

SELECT Tag.Title, count(*)
  FROM Tag
 GROUP BY Tag.Title

Produkty dla jednego tagu:

3-stół:

SELECT Item.*
  FROM Item
  JOIN ItemTag ON Item.ItemID = ItemTag.ItemID
  JOIN Tag ON ItemTag.TagID = Tag.TagID
 WHERE Tag.Title = :title

2-stół:

SELECT Item.*
  FROM Item
  JOIN Tag ON Item.ItemID = Tag.ItemID
 WHERE Tag.Title = :title

Ale są też pewne wady: może zająć więcej miejsca w bazie danych (co może prowadzić do większej liczby operacji na dysku, co jest wolniejsze) i nie jest znormalizowany, co może prowadzić do niespójności.

Argument wielkości nie jest tak silny, ponieważ sama natura tagów polega na tym, że zwykle są one dość małe, więc wzrost rozmiaru nie jest duży. Można argumentować, że zapytanie o tytuł znacznika jest znacznie szybsze w małej tabeli, która zawiera każdy znacznik tylko raz, a to z pewnością jest prawdą. Ale biorąc pod uwagę oszczędności wynikające z rezygnacji z przyłączenia się oraz fakt, że można na nich zbudować dobry indeks, można to łatwo zrekompensować. Zależy to oczywiście w dużej mierze od wielkości używanej bazy danych.

Argument o niekonsekwencji jest również trochę dyskusyjny. Tagi są polami tekstowymi i nie ma oczekiwanej operacji, takiej jak „zmień nazwę wszystkich tagów„ foo ”na„ bar ”.

Więc tldr: wybrałbym rozwiązanie z dwoma stołami. (W rzeczywistości zamierzam. Znalazłem ten artykuł, aby sprawdzić, czy istnieją uzasadnione argumenty przeciwko niemu.)

Scheintod
źródło
Czy „Index: ItemId, Title” oznacza indeks dla każdego lub jednego indeksu zawierającego oba?
DanMan
Zwykle dwa indeksy. Może to zależeć od używanej bazy danych.
Scheintod
1
Czy w tabeli znaczników jest pozycja ItemId i Tag złożony klucz? czy też masz PK?
Rippo
2
w ten sposób nie można tworzyć „nieużywanych” tagów, dlatego na elemencie należy wykonać funkcję „dodaj tag”. Z drugiej metody funkcję „dodaj tag” można wykonać niezależnie
Gianluca Ghettini
1
@Quilang. Nadal uważam, że to zależy od tego, co porabiasz :) Zaimplementowałem to na dwa sposoby w różnych projektach. W moim ostatnim skończyło się na rozwiązaniu z trzema tabelami, ponieważ potrzebowałem „typu tagu” (lub kilku innych meta informacji na tagu) i mogłem ponownie użyć kodu od bliskiego kuzyna tagów: parametrów. Ale w tym samym projekcie zastosowałem dokładnie tę metodę dla jeszcze bliższego kuzyna: flagi (np. „Sprzedane”, „nowe”, „gorące”)
Scheintod
38

Jeśli korzystasz z bazy danych obsługującej redukcję mapy, takiej jak couchdb, przechowywanie znaczników w polu tekstowym lub polu listy jest rzeczywiście najlepszym sposobem. Przykład:

tagcloud: {
  map: function(doc){ 
    for(tag in doc.tags){ 
      emit(doc.tags[tag],1) 
    }
  }
  reduce: function(keys,values){
    return values.length
  }
}

Uruchomienie tego z group = true grupuje wyniki według nazwy znacznika, a nawet zwraca liczbę przypadków napotkania znacznika. Jest to bardzo podobne do liczenia występowania słowa w tekście .

Nick Retallack
źródło
4
+1 Miło widzieć także niektóre implementacje NoSQL.
Xeoncross
@NickRetallack Link nie działa. Jeśli możesz, zaktualizuj tę odpowiedź.
xralf
Ok, zastąpiłem link linkiem do archive.org
Nick Retallack,
13

Użyj pojedynczej sformatowanej kolumny tekstowej [1] do przechowywania znaczników i użyj zdolnej wyszukiwarki pełnotekstowej do jej indeksowania. W przeciwnym razie napotkasz problemy ze skalowaniem podczas próby implementacji zapytań boolowskich.

Jeśli potrzebujesz szczegółowych informacji o posiadanych tagach, możesz śledzić je w przyrostowo utrzymywanej tabeli lub uruchomić zadanie wsadowe w celu wyodrębnienia informacji.

[1] Niektóre RDBMS zapewniają nawet natywny typ macierzy, który może być jeszcze lepiej przystosowany do przechowywania, ponieważ nie wymaga etapu analizy, ale może powodować problemy z wyszukiwaniem pełnotekstowym.

David Schmitt
źródło
Czy znasz wyszukiwarkę pełnotekstową, która nie znajdzie odmian słowa? Na przykład wyszukiwanie książek zwraca książki? Co robisz z tagami typu „c ++”? Na przykład SQL Server usunie znaki plus w indeksie. Dzięki.
Jonathan Wood
Wypróbuj Sphinx - sphinxsearch.com
Roman
Ten 3-częściowy samouczek może być przydatny dla osób, które wybierają tę trasę (wyszukiwanie pełnotekstowe). Korzysta z natywnych narzędzi PostgreSQL: shisaa.jp/postset/postgresql-full-text-search-part-1.html
Czy
czy jest to lepsze niż wybrana odpowiedź pod względem wydajności?
co powiesz na przechowywanie przy użyciu varchar 255, rozdzielanie przecinków i dodawanie do niego indeksu tekstowego kfull?
9

Zawsze trzymałem tagi w osobnej tabeli, a potem miałem tabelę mapowania. Oczywiście, nigdy nie zrobiłem nic na naprawdę dużą skalę.

Posiadanie tabeli „znaczników” i tabeli map sprawia, że ​​generowanie chmur znaczników jest bardzo proste, ponieważ można łatwo łączyć SQL, aby uzyskać listę znaczników z licznikiem częstotliwości używania każdego znacznika.

Mark Biek
źródło
6
Jest to jeszcze łatwiejsze, jeśli nie używasz tabeli mapowania :)
Scheintod
0

Sugerowałbym następujący projekt: Tabela przedmiotów: Itemid, taglist1, taglist2
to będzie szybkie i ułatwi zapisywanie i pobieranie danych na poziomie przedmiotu.

Równolegle buduj inną tabelę: Tag tagów nie powoduje, że tag jest unikalnym identyfikatorem, a jeśli zabraknie miejsca w drugiej kolumnie, która zawiera, powiedzmy 100 elementów tworzy kolejny wiersz.

Teraz podczas wyszukiwania elementów dla tagu będzie super szybko.

użytkownik236575
źródło
en.wikipedia.org/wiki/First_normal_form chociaż są wyjątki od tego, możesz zdenormalizować, ale nie tutaj
Dheeraj