将“毒性”方式规范化是否真的值得? (3NF)

时间:2010-01-14 11:52:21

标签: sql mysql optimization normalization

我正处于数据库设计的早期阶段,所以还没有最终结果,我正在使用“TOXI”3表设计作为我的线程,它有可选的标签,但我不禁感到加入并不是必需的,也许我需要依赖于posts表中的一个简单的标签列,我可以在其中存储类似<tag>, <secondTag>的varchar。

所以回顾一下:

  • 是否值得在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 个答案:

答案 0 :(得分:6)

将所有标记放在不同的记录中(标准化)意味着您可以在需要时更轻松地重命名标记并跟踪标记名称历史记录。

例如,

SO至少重命名SQL Server个相关标签三次(mssql - &gt; sqlserver - &gt; sql-server)。

将所有标记放在一个记录中(非规范化)意味着您可以使用FULLTEXT索引为此列编制索引,并一次搜索包含两个或多个标记的帖子:

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

这也是可能的,但通过标准化设计效率较低。

(不要忘记将@ft_min_word_len调整为3字符或更少字符的索引,以使其正常工作)

您可以组合两种设计:存储地图表和非规范化列。但这需要更多的维护。

您还可以将规范化设计存储在数据库中,并使用您提供的查询将代码提供给SphinxLucene

这样,您可以使用MySQL进行历史记录挖掘,使用Sphinx进行全文标记搜索,无需额外维护。

答案 1 :(得分:3)

如果你有一个带有标签列表的varchar,那么你对标签的查询会非常慢。你会在post.tag like '%mytag%'所在的地方做一些事情,这些事情不会在任何地方执行,也不会搜索到索引键。

[编辑] 这项研究显示了performance各种标记系统的方法(包括FULLTEXT索引),并建议你在何时何地使用每一种方法。

答案 2 :(得分:3)

如果使用VARCHAR hack,则几乎不可能查询数据。编写一个查询可以准确有效地显示具有给定标记的所有帖子(并让我们面对它,这是标记系统的一个非常重要的方面)将是完美的:准确性部分很难,因为您需要考虑所有可能性逗号;效率部分很难,因为在字符串中搜索比查看字段的完整值要慢得多(如果可以使用整数,则更慢)。

所以是的,这当然是值得的。

就提高查询速度而言 - 确保在表上有相关索引。对查询运行EXPLAIN以查看放置瓶颈的位置。我不认为在处理每个帖子时获取标签会更好,但它可能是 - 我不确定MySQL在字符串操作方面的效率如何,这就是你在做GROUP_CONCAT时所做的事情。

答案 3 :(得分:2)

加入(当你有正确的索引时)通常比尝试从字段中的逗号分隔字符串中间提取数据要快得多,即使使用全文搜索也是如此。或者您可以使用一堆单独的标记字段(Tag1,tag2,tag3),查询仍然会更难(让我搜索5个字段以查找是否已使用该标记)并且您需要每次添加一个新列您需要添加新标记,并且已经用完了现有列。规范化的数据库设计是最好的,最高效的方式。数据库旨在使用连接。为什么你不想使用它们超出我的范围。