Действительно ли стоит нормализовать путь «токси»? (3NF) - PullRequest
5 голосов
/ 14 января 2010

Я нахожусь на ранних стадиях разработки своей базы данных, поэтому пока ничего не является окончательным, и я использую дизайн с тремя таблицами "TOXI" для моих потоков, которые имеют дополнительные теги, но я не могу не чувствовать, что объединение не является действительно необходимым, и, возможно, мне нужно просто положиться на простой столбец тегов в моей таблице posts, где я могу просто хранить varchar чего-то вроде <tag>, <secondTag>.

Итак, резюмируем:

  • стоит ли тратить лишние левые объединения на 2 таблицы тегов вместо того, чтобы просто иметь столбец тегов в моей таблице posts.
  • Есть ли способ оптимизировать мой запрос?

Схема

CREATE TABLE `posts` (
    `post_id` INT UNSIGNED PRIMARY AUTO_INCREMENT,
    `post_name` VARCHAR(255)
) Engine=InnoDB;

CREATE TABLE `post_tags` (
    `tag_id` INT UNSIGNED PRIMARY AUTO_INCREMENT,
    `tag_name` VARCHAR(255)
) Engine=InnoDB;

CREATE TABLE `post_tags_map` (
    `map_id` INT PRIMARY AUTO_INCREMENT,
    `post_id` INT NOT NULL,
    `tags_id` INT NOT NULL,
    FOREIGN KEY `post_id` REFERENCES `posts` (`post_id`),
    FOREIGN KEY `post_id` REFERENCES `post_tags` (`tag_id`)
) Engine=InnoDB;

Пример данных

INSERT INTO `posts` (`post_id`, `post_name`)
  VALUES
(1, 'test');

INSERT INTO `post_tags` (`tag_id`, `tag_name`)
  VALUES
(1, 'mma'),
(2, 'ufc');

INSERT INTO `posts_tags_map` (`map_id`, `post_id`, `tags_id`)
  VALUES
(1, 1, 1),
(2, 1, 2);

Текущий запрос

SELECT 
    posts.*,
    GROUP_CONCAT( post_tags.tag_name order by post_tags.tag_name ) AS tags

  FROM posts
    LEFT JOIN posts_tags_map
      ON posts_tags_map.post_id = posts.post_id
    LEFT JOIN post_tags
      ON posts_tags_map.tags_id = posts_tags.tag_id

  WHERE posts.post_id = 1
  GROUP BY post_id

Результат

IF есть теги:

post_id     post_name        tags
1             test           mma, ufc

Ответы [ 4 ]

6 голосов
/ 14 января 2010

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

SO, например, переименовано SQL Server связанных тегов как минимум трижды (mssql -> sqlserver -> sql-server).

Наличие всех тегов в одной записи (денормализовано) означает, что вы можете индексировать этот столбец с индексом FULLTEXT и искать записи, содержащие два или более тегов одновременно:

SELECT  *
FROM    posts
WHERE   MATCH(tags) AGAINST('+mma +ufc')

, что тоже возможно, но менее эффективно при нормализованном дизайне.

(Не забудьте настроить @ft_min_word_len, чтобы индексировать теги из 3 символов или менее, чтобы это работало)

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

Вы также можете сохранить нормализованный дизайн в вашей базе данных и использовать предоставленный вами запрос для передачи тегов Sphinx или Lucene.

Таким образом, вы можете копать историю с помощью MySQL, полнотекстовый поиск по тегам с использованием Sphinx, и никакого дополнительного обслуживания не потребуется.

3 голосов
/ 14 января 2010

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

Так что да, это, безусловно, того стоит.

Что касается ускорения запроса - убедитесь, что в ваших таблицах есть соответствующие индексы. Запустите EXPLAIN для запроса, чтобы увидеть, где находится какое-либо узкое место. Я не думаю, что было бы лучше извлекать теги для каждого сообщения во время его обработки, но это может быть - я не уверен, насколько MySQL действительно эффективен при работе со строками, что он и делает, когда вы делаете GROUP_CONCAT .

3 голосов
/ 14 января 2010

Ваш запрос тега был бы очень медленным, если бы у вас был varchar со списком тегов. Вы бы делали что-то вроде строки post.tag like '%mytag%', которая не выполнялась бы поблизости, а также выполнял поиск по проиндексированным ключ.

[править] Это исследование показывает производительность различных способов создания систем тегов (включая индексированные FULLTEXT) и предлагает, где и когда вы хотели бы использовать каждый из них.

2 голосов
/ 14 января 2010

Объединение (если у вас правильные индексы), как правило, выполняется намного быстрее, чем попытка извлечь данные из середины строки, разделенной запятой, в поле, даже используя полнотекстовый поиск. Или вы можете использовать несколько отдельных полей тегов (Tag1, tag2, tag3), и запросы будут по-прежнему сложнее (позвольте мне искать в 5 полях, чтобы найти, использовал ли я этот тег), и вам нужно будет каждый раз добавлять новый столбец вам нужно добавить новый тег, и вы уже использовали существующие столбцы. Нормализованный дизайн базы данных - это наилучший и наиболее эффективный способ. Базы данных предназначены для использования объединений. Почему ты не хочешь их использовать, я не знаю.

...