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

288

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

Я пропускаю лучшую практику для тегов?

dlamblin
источник
9
Хорошо, это вопрос № 20856, (почти) тот же вопрос # 48475 задавался по крайней мере через две недели после того, как был задан этот вопрос.
Дламблин
9
Еще один интересный вопрос: как SO реализует теги?
Мостафа
1
Другой интересный вопрос: «Не могли бы вы их интернационализировать, и если да, то как?»
DanMan
1
Интересное сравнение (специфично для Postgres): databaseoup.com/2015/01/tag-all-things.html
a_horse_with_no_name
1
см. также stackoverflow.com/questions/48475/database-design-for-tagging
Ян Рингроз

Ответы:

406

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

Table: Item
Columns: ItemID, Title, Content

Table: Tag
Columns: TagID, Title

Table: ItemTag
Columns: ItemID, TagID
Яаков Эллис
источник
32
Это решение называется «Toxi», дополнительную информацию о нем можно найти здесь: howto.philippkeller.com/2005/04/24/Tags-Database-schemas
The Pixel Developer,
16
Единственное, что здесь не показано, это иерархические «теги» или категории в таблице «Теги». Это обычно требуется на сайтах, которые имеют категории и подкатегории, но нуждаются в гибкости тегов. Например, сайты рецептов, сайты автозапчастей, бизнес-каталоги и т. Д. Эти типы данных обычно не вписываются только в одну категорию, поэтому тегирование - это ответ, но вам нужно использовать что-то вроде модели вложенного набора или модели списка смежности. в вашей таблице тегов.
HK1
5
Я согласен с HK1, возможно ли это с приведенной выше структурой + Таблица: Столбцы TagGroup: TagGropuId, Таблица заголовков: Столбцы тегов: TagID, Title, TagGroupId
Thunder
Когда я хочу добавить столбец CSS в таблицу, я добавлю столбец CSS в таблицу тегов?
Амитабха
10
@ftvs: ссылка снова не работает, новая ссылка - howto.philippkeller.com/2005/04/24/Tags-Database-schemas
hansaplast
83

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

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

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: я бы пошел на решение двух столов. (На самом деле я собираюсь. Я нашел эту статью, чтобы увидеть, есть ли веские аргументы против этого.)

Scheintod
источник
Означает ли «Index: ItemId, Title» индекс для каждого или один индекс, содержащий оба?
DanMan
Обычно два индекса. Может зависеть от базы данных, которую вы используете.
Scheintod
1
В таблице тегов есть ItemId и Tag составной ключ? или у тебя тоже есть ПК?
Rippo
2
таким образом, вы не можете создавать «неиспользуемые» теги, поэтому для элемента необходимо выполнить функцию «добавить тег». По другому методу функцию «добавить тег» можно выполнить независимо
Джанлука Геттини,
1
@Quilang. Я все еще верю, что это зависит от того, чем вы занимаетесь :) Я реализовал это обоими способами в разных проектах. В моем последнем случае я получил решение с тремя таблицами, потому что мне требовался «тип тега» (или некоторая другая метаинформация о теге), и я мог повторно использовать некоторый код от близкого родственника тегов: параметров. Но в том же проекте я использовал именно этот метод для еще более близкого родственника: флаги (например, «продано», «новый», «горячий»)
Scheintod
38

Если вы используете базу данных, которая поддерживает 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 Приятно также видеть некоторые реализации NoSQL.
Xeoncross
@NickRetallack Ссылка не работает. Если вы могли бы, пожалуйста, обновите этот ответ.
xralf
Хорошо, я заменил ссылку одной на archive.org
Ник Реталлак
13

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

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

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

Дэвид Шмитт
источник
Вам известна какая-либо полнотекстовая поисковая система, которая не находит вариации в слове? Например, поиск книг возвращает книги? Кроме того, что вы делаете с тегами типа "c ++"? SQL Server, например, лишил бы знаки плюс в индексе. Спасибо.
Джонатан Вуд
Попробуйте Сфинкса - sphinxsearch.com
Roman
Этот учебник из трех частей может быть полезен для тех, кто идет по этому пути (полнотекстовый поиск). Он использует собственные возможности PostgreSQL: shisaa.jp/postset/postgresql-full-text-search-part-1.html
Уилл
это лучше, чем выбранный ответ с точки зрения производительности?
как насчет хранения в varchar 255 тегов, разделенных запятыми, и добавления к ним текстового индекса kfull?
9

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

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

Марк Бик
источник
6
Это еще проще, если вы не используете таблицу сопоставления :)
Scheintod
0

Я бы предложил следующий дизайн: Item Table: Itemid, taglist1, taglist2
это будет быстро и позволит легко сохранять и извлекать данные на уровне элемента.

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

Теперь при поиске предметов по тегу это будет супер быстро.

user236575
источник
ru.wikipedia.org/wiki/First_normal_form, хотя есть исключения из этого, вы можете денормализовать, но не здесь
Dheeraj