Рекомендуемый дизайн базы данных SQL для тегов или пометок [закрыт]
-
09-06-2019 - |
Вопрос
Я слышал о нескольких способах реализации тегирования;использование таблицы сопоставления между 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 элементов, создайте еще одну строку.
Теперь при поиске товаров по тегу это будет происходить очень быстро.