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


288

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

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


9
Гаразд, це питання №20856, (майже) те саме питання # 48475 було задано щонайменше через два тижні після того, як було задано це питання.
dlamblin

9
Ще одне цікаве питання - "Як ТАК реалізує теги?"
Мостафа

1
Ще одне цікаве запитання: "Чи б ви їх інтернаціоналізували, і якщо так, то як?"
DanMan

1
Цікаве порівняння (специфічне для Postgres): databasesoup.com/2015/01/tag-all-things.html
a_horse_with_no_name

Відповіді:


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
Розробник Pixel

16
Одне, що не показано тут, - це ієрархічні "теги" або категорії в таблиці Тегів. Це зазвичай потрібно на сайтах, які мають категорії та підкатегорії, але потребують гнучкості тегів. Наприклад, сайти з рецептами, сайти з автозапчастинами, бізнес-каталоги тощо. Ці типи даних зазвичай не вписуються лише в одну категорію, тому тегування є відповіддю, але вам потрібно використовувати щось на зразок вкладеної моделі набору або моделі списку суміжності у вашій таблиці тегів.
HK1

5
Я погоджуюсь з HK1, чи можливо з вищевказаною структурою + Таблиця: Стовпці тегівGroup: TagGropuId, Таблиця заголовків: Стовпчики тегів: TagID, Назва, TagGroupId
Грім

коли я хочу додати стовпчик 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: Я б пішов на рішення двох столів. (Насправді я збираюся. Я знайшов цю статтю, щоб побачити, чи є вагомі аргументи проти неї.)


Чи означає "Index: ItemId, Title" індекс для кожного або одного індексу, що містить обидва?
DanMan

Зазвичай два індекси. Це може залежати від бази даних, яку ви використовуєте.
Scheintod

1
У таблиці тегів - ItemId і Tag - складений ключ? чи у вас також є ПК?
Ріппо

2
таким чином ви не можете створити "невикористані" теги, тому функція "Додати тег" повинна бути виконана на Елементі. В іншому способі функцію "додати тег" можна виконати самостійно
Gianluca Ghettini

1
@Quilang. Я все ще вважаю, що це залежить від того, що ти робиш :) Я реалізував це обома способами в різних проектах. У своєму останньому я опинився з рішенням таблиці 3, тому що мені потрібен був "тип тегу" (або якась інша метаінформація про тег) і я міг повторно використовувати якийсь код у близького родича тегів: параметрів. Але в тому самому проекті я застосував саме цей метод для ще більш близького двоюрідного брата: прапори (напр., "Продано", "нове", "гаряче")
Scheintod

38

Якщо ви використовуєте базу даних, яка підтримує зменшення карт, наприклад couchdb, зберігання тегів у простому текстовому полі чи полі списку - це справді найкращий спосіб. Приклад:

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

Якщо запустити це з групою = true, результати згрупуються за назвою тегів і навіть повернуть підрахунок кількості разів, з якими стикався тег. Це дуже схоже на підрахунок зустрічей слова в тексті .


4
+1 Приємно також бачити деякі реалізації NoSQL.
Xeoncross

@NickRetallack Посилання не працює. Якщо можете, оновіть цю відповідь.
xralf

Гаразд я замінив посилання на архів.org
Нік Реталлак,

13

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

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

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


Чи знаєте ви будь-якій повнотекстовій пошуковій системі, яка не знаходить варіацій слова? Наприклад, пошук книг повертає книги? Крім того, що ви робите з тегами типу "c ++"? Наприклад, SQL Server позбавить знаків плюс в індексі. Дякую.
Джонатан Вуд

Спробуйте Сфінкс - sphinxsearch.com
Роман

Цей підручник з трьох частин може бути корисним для тих, хто проходить цей маршрут (пошук у повному тексті). Це з використанням PostgreSQL вбудованих засобів: shisaa.jp/postset/postgresql-full-text-search-part-1.html
Воля

це краще, ніж обрана відповідь з точки зору продуктивності?

як щодо зберігання у використанні varchar 255, розділених комами тегів та додавання до нього повного текстового індексу?

9

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

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


6
Це ще простіше, якщо ви не використовуєте таблицю відображення :)
Scheintod

0

Я б запропонував наступний дизайн: Таблиця предметів: Itemid, taglist1, taglist2,
це буде швидко та спростить збереження та отримання даних на рівні елемента.

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

Зараз під час пошуку елементів для тегу це буде дуже швидко.


en.wikipedia.org/wiki/First_normal_form, хоча з цього є винятки, ви можете денормалізувати, але не тут
Dheeraj
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.