Рекомендуемый дизайн базы данных SQL для тегов или тегов

Я слышал о нескольких способах реализации тегов; используя таблицу сопоставления между TagID и ItemID (имеет смысл для меня, но делает ли это масштаб?), добавляя фиксированное количество возможных столбцов TagID к ItemID (кажется плохой идеей), сохраняя теги в текстовом столбце, разделенном запятыми (звучит сумасшедший, но может работать). Я даже слышал, что кто-то порекомендовал разреженную матрицу, но тогда как имена тегов становятся грациозными?

Не хватает ли лучшей практики для тэгов?

Ответ 1

Три таблицы (один для хранения всех элементов, один для всех тегов и один для отношения между ними), должным образом проиндексированный, с внешними ключами, установленными в соответствующей базе данных, должны хорошо работать и масштабироваться должным образом.

Table: Item
Columns: ItemID, Title, Content

Table: Tag
Columns: TagID, Title

Table: ItemTag
Columns: ItemID, TagID

Ответ 2

Обычно я соглашался с Яаком Эллисом, но в этом частном случае существует еще одно жизнеспособное решение:

Используйте две таблицы:

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

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

Это имеет ряд основных преимуществ:

Сначала он значительно упрощает разработку: в решении из трех таблиц для вставки и обновления item вам нужно найти таблицу Tag, чтобы увидеть, есть ли уже записи. Тогда вы должны присоединиться к ним с новыми. Это не тривиальная задача.

Затем он делает запросы проще (и, возможно, быстрее). Вы должны выполнить три основных запроса к базе данных: вывести все Tags для одного item, нарисовать Tag-Cloud и выбрать все элементы для одного заголовка тега.

Все теги для одного элемента:

3-таблица:

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

2-таблицы:

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

Tag-Cloud:

3-таблица:

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

2-таблицы:

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

Элементы для одного тега:

3-таблица:

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

2-таблицы:

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

Но есть и некоторые недостатки: он может занимать больше места в базе данных (что может привести к большему количеству операций с дисками, которое медленнее), и оно не нормализовалось, что может привести к несоответствиям.

Аргумент размера не настолько силен, потому что сама природа тегов заключается в том, что они обычно довольно маленькие, поэтому увеличение размера не является большим. Можно утверждать, что запрос названия тега намного быстрее в маленькой таблице, которая содержит каждый тег только один раз, и это, безусловно, верно. Но принимая во внимание экономию за то, что вы не должны присоединиться, и тот факт, что вы можете построить хороший индекс, может легко компенсировать это. Это, конечно, сильно зависит от размера используемой вами базы данных.

Аргумент несогласованности тоже немного спор. Теги являются свободными текстовыми полями, и нет ожидаемой операции, такой как "переименовать все теги" foo "в" bar ".

Итак, TL;DR: Я бы выбрал решение с двумя таблицами. (На самом деле я собираюсь. Я нашел эту статью, чтобы увидеть, существуют ли против нее допустимые аргументы.)

Ответ 3

Если вы используете базу данных, поддерживающую map-reduce, например couchdb, сохранение тегов в текстовом поле или в поле списка действительно лучший способ. Пример:

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

Выполнение этого с помощью group = true будет группировать результаты по имени тега и даже возвращать счетчик количества раз, когда этот тег встречался. Это очень похоже на подсчет вхождения слова в текст.

Ответ 4

Используйте один форматированный текстовый столбец [1] для хранения тегов и используйте способную полнотекстовую поисковую систему для индексации этого. Иначе вы столкнетесь с проблемами масштабирования при попытке реализовать логические запросы.

Если вам нужны детали о ваших тегах, вы можете отслеживать их в таблице с постепенным сохранением или запускать пакетное задание для извлечения информации.

[1] Некоторые RDBMS даже предоставляют собственный тип массива, который может быть даже лучше подходит для хранения, не нуждаясь в синтаксическом анализе, но может вызвать проблемы с полным текстовым поиском.

Ответ 5

Я всегда хранил теги в отдельной таблице, а затем имел таблицу сопоставления. Конечно, я никогда не делал ничего в действительно больших масштабах.

Наличие таблицы "тегов" и таблицы карт делает довольно тривиальным создание облаков тегов и т.д., поскольку вы можете легко собрать SQL для получения списка тегов с подсчетом того, как часто используется каждый тег.

Ответ 6

Я бы предложил следующий дизайн: Таблица позиций: Itemid, taglist1, taglist2
это будет быстро и упростит сохранение и извлечение данных на уровне позиции.

Параллельно создайте другую таблицу: Теги тег не делайте уникальный идентификатор тега, и если у вас закончится пробел во втором столбце, который содержит позволяет сказать, что 100 элементов создают другую строку.

Теперь при поиске элементов для тега он будет очень быстрым.