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

StackOverflow https://stackoverflow.com/questions/20856

Вопрос

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

Я упускаю из виду рекомендации по использованию тегов?

Это было полезно?

Решение

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

Table: Item
Columns: ItemID, Title, Content

Table: Tag
Columns: TagID, Title

Table: ItemTag
Columns: ItemID, TagID

Другие советы

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

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

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

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

Это имеет несколько основных преимуществ:

Во-первых, это значительно упрощает разработку:в решении из трех таблиц для вставки и обновления item вы должны просмотреть Tag таблица, чтобы увидеть, есть ли уже записи.Затем вы должны объединить их с новыми.Это нетривиальная задача.

Тогда это упрощает запросы (и, возможно, быстрее).Есть три основных запроса к базе данных, которые вы будете выполнять:Вывести все Tags для одного Item, нарисуйте облако тегов и выберите все элементы для одного заголовка тега.

Все теги для одного товара:

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

Облако тегов:

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"".

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

Если вы используете базу данных, поддерживающую 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 сгруппирует результаты по имени тега и даже вернет количество раз, когда этот тег встречался.Это очень похоже на подсчет встречаемости слова в тексте.

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top