6

编辑:对于构建标记系统的人。不要读这个。这不是你要找的。当我不知道RDBMS都有自己的优化方法时,我问了这个问题,只需使用简单的多对多方案即可。

我有一个拥有数百万个帖子的发布系统。每个帖子可以有无数个与之关联的标签。

用户可以创建包含注释、创建日期、所有者等的标签。标签几乎就像帖子本身,因为人们可以发布关于标签的注释。

每个标签关联都有一个所有者和日期,因此我们可以看到谁添加了标签以及何时添加。

我的问题是如何实现这一点?它必须通过标签或标签快速搜索帖子。此外,用户可以通过在字段中输入名称来为帖子添加标签,有点像谷歌搜索栏,它必须为您填写标签名称的其余部分。

我目前有 3 个解决方案,但不确定哪个是最好的,或者是否有更好的方法。

请注意,我没有显示笔记的布局,因为一旦我获得了适当的标签解决方案,这将是微不足道的。

方法一、链表

post中的tagId指向tag_assoc中的一个链表,应用程序必须遍历链表直到flink=0

post:           id, content, ownerId, date, tagId, notesId
tag_assoc:      id, tagId, ownerId, flink
tag:            id, name, notesId

方法 2. 非规范化

tags 只是一个 VARCHAR 或 TEXT 字段,其中包含一个制表符分隔的 tagId:ownerId 数组。它不能是固定大小。

post:           id, content, ownerId, date, tags, notesId
tag:            id, name, notesId

方法 3. 毒物

(来自:http ://www.pui.ch/phred/archives/2005/04/tags-database-schemas.html ,这里也一样:标签或标记的推荐 SQL 数据库设计

post:          id, content, ownerId, date, notesId
tag_assoc:     ownerId, tagId, postId
tag:           id, name, notesId

方法 3 提出了一个问题,遍历 tag_assoc 中的每一行的速度有多快?

方法 1 和 2 对于按帖子返回标签应该很快,但是对于按标签的帖子,必须制作另一个查找表。

我要担心的最后一件事是按名称优化搜索标签,我还没有解决这个问题。

我在这里做了一个ASCII图:http: //pastebin.com/f1c4e0e53

4

4 回答 4

2

这是我的做法:

posts:          [postId], content, ownerId, date, noteId, noteType='post'
tag_assoc:      [postId, tagName], ownerId, date, noteId, noteType='tagAssoc'
tags:           [tagName], ownerId, date, noteId, noteType='tag'
notes:          [noteId, noteType], ownerId, date, content

方括号中的字段是相应表的主键。

noteType在每个表中定义一个约束: poststag_assoctags。例如,这可以防止给定的注释同时应用于 apost和 a tag

将标签名称存储为短字符串,而不是整数id。这样您就可以使用表中的覆盖索引 [ postId, tagName] tag_assoc

使用 AJAX 调用完成标记完成。如果用户为标签键入“datab”,您的网页会进行 AJAX 调用,并且在服务器端,应用程序会查询:SELECT tagName FROM tags WHERE tagName LIKE ?||'%'

于 2009-03-21T01:47:17.607 回答
0

“标签几乎就像一个帖子本身,因为人们可以发布关于标签的注释。” - 这句话让我觉得你真的只想要一个用于 POST 的表,带有一个主键和一个引用 POST 表的外键。现在,您可以在磁盘空间允许的范围内为每个帖子添加尽可能多的标签。

我假设 POST 和标签之间不需要多对多,因为标签不会在帖子之间共享,基于此:

“用户可以创建包含注释、创建日期、所有者等的标签。”

如果创建日期和所有者是共享的,那将是两个额外的外键关系,IMO。

于 2009-03-19T22:49:38.367 回答
0

链表几乎可以肯定是错误的方法。这当然意味着您的查询将是复杂的或次优的 - 这具有讽刺意味,因为使用链表的最可能原因是将数据保持在正确的排序顺序中。但是,我没有看到一种简单的方法来避免迭代地获取一行,然后使用检索到的 flink 值来调整下一行的选择操作。

因此,请使用基于表的方法,将普通外键用于主键引用。比尔卡尔文概述的那个看起来与我概述的相似。

于 2009-03-21T17:59:00.110 回答
0

比尔,我想我有点把你甩了,笔记就在另一张桌子上,还有一张单独的桌子,上面有不同人张贴的笔记。帖子有注释和标签,但标签也有注释,这就是标签是唯一的原因。

Jonathan 对链表的看法是正确的,我根本不会使用它们。我决定以最简单的规范化方式实现标签,以满足我的需求:

DROP TABLE IF EXISTS `tags`;
CREATE TABLE IF NOT EXISTS `tags` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `owner` int(10) unsigned NOT NULL,
  `date` int(10) unsigned NOT NULL,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

DROP TABLE IF EXISTS `posts`;
CREATE TABLE IF NOT EXISTS `posts` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `owner` int(10) unsigned NOT NULL,
  `date` int(10) unsigned NOT NULL,
  `name` varchar(255) NOT NULL,
  `content` TEXT NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

DROP TABLE IF EXISTS `posts_notes`;
CREATE TABLE IF NOT EXISTS `posts_notes` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `owner` int(10) unsigned NOT NULL,
  `date` int(10) unsigned NOT NULL,
  `postId` int(10) unsigned NOT NULL,
  `note` TEXT NOT NULL,
  PRIMARY KEY (`id`),
  FOREIGN KEY (`postId`) REFERENCES posts(`id`) ON DELETE CASCADE
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

DROP TABLE IF EXISTS `posts_tags`;
CREATE TABLE IF NOT EXISTS `posts_tags` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `owner` int(10) unsigned NOT NULL,
  `tagId` int(10) unsigned NOT NULL,
  `postId` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  FOREIGN KEY (`postId`) REFERENCES posts(`id`) ON DELETE CASCADE,
  FOREIGN KEY (`tagId`) REFERENCES tags(`id`) ON DELETE CASCADE
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

我不确定这在未来会有多快,但在一段时间内应该没问题,因为只有几个人使用数据库。

于 2009-03-22T01:43:20.027 回答