编辑:建立标记系统的人。不读这个。这不是你想要的。当我不知道RDBMS都有自己的优化方法时,我问过这个问题,只需使用简单的多对多方案。
我的帖子系统有数百万个帖子。每个帖子都可以有无数个与之关联的标签。
用户可以创建包含备注,创建日期,所有者等的标签。标签几乎就像一个帖子本身,因为人们可以发布有关标签的注释。
每个标记关联都有一个所有者和日期,因此我们可以看到谁添加了标记以及何时添加。
我的问题是如何实现这一点?它必须通过标签快速搜索帖子,或通过邮寄标签。此外,用户可以通过在字段中键入名称来为帖子添加标签,有点像谷歌搜索栏,它必须为您填写标签名称的其余部分。
目前我有3个解决方案,但不确定哪个是最好的,或者是否有更好的方法。
请注意,我没有显示笔记的布局,因为一旦我得到适当的标签解决方案,这将是微不足道的。
方法1.链接列表
帖子中的tagId指向tag_assoc中的链表,应用程序必须遍历列表,直到flink = 0
post: id, content, ownerId, date, tagId, notesId
tag_assoc: id, tagId, ownerId, flink
tag: id, name, notesId
方法2.非规范化
标签只是一个VARCHAR或TEXT字段,包含tagId:ownerId的制表符分隔数组。它不能是固定的大小。
post: id, content, ownerId, date, tags, notesId
tag: id, name, notesId
方法3. Toxi
(来自:http://www.pui.ch/phred/archives/2005/04/tags-database-schemas.html, 同样的事情:Recommended SQL database design for tags or tagging)
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
答案 0 :(得分: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
定义约束:posts
,tag_assoc
和tags
。例如,这可以防止给定的注释同时适用于post
和tag
。
将标记名称存储为短字符串,而不是整数id
。这样您就可以在postId
表中使用覆盖索引[tagName
,tag_assoc
]。
使用AJAX调用完成标记完成。如果用户为标记键入“datab”,则您的网页会进行AJAX调用,而在服务器端,应用程序会查询:SELECT tagName FROM tags WHERE tagName LIKE ?||'%'
。
答案 1 :(得分:0)
“标签几乎就像一个帖子本身,因为人们可以发布关于标签的说明。” - 这个短语让我觉得你真的只想要一个用于POST的表,一个主键和一个引用POST表的外键。现在,您可以为磁盘空间允许的每个帖子添加尽可能多的标记。
我假设POST和标签之间不需要多对多,因为标签不会在帖子之间共享,基于此:
“用户可以创建包含备注,创建日期,所有者等的标签”
如果共享创建日期和所有者,那么这将是另外两个外键关系,IMO。
答案 2 :(得分:0)
链接列表几乎肯定是错误的方法。这当然意味着您的查询将是复杂的或次优的 - 这具有讽刺意味,因为使用链表的最可能原因是将数据保持在正确的排序顺序。但是,我没有看到一种简单的方法来避免迭代地获取行,然后使用检索的flink值来调整下一行的select操作。
因此,使用基于表的方法,使用普通外键进行主键引用。 Bill Karwin概述的那个与我概述的相似。
答案 3 :(得分:0)
乔纳森对链接列表是正确的,我根本不会使用它们。我决定以最简单的标准化方式实现标签,以满足我的需求:
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 ;
我不确定将来会有多快,但它应该会好一段时间,因为只有几个人使用数据库。