在SQL中存储线程消息列表/树的最佳方法是什么?

时间:2009-03-26 23:59:36

标签: sql

我正在寻找存储一组“帖子”的最佳方式,以及对SQL中这些帖子的评论。想象一下类似于Facebook上的“墙”的设计,用户可以在墙上写帖子,其他用户可以对这些帖子发表评论。我需要能够显示所有墙上的帖子以及评论。

当我刚开始时,我想出了一个表格,如:

CREATE Table wallposts
(
 id uuid NOT NULL,
 posted timestamp NOT NULL,
 userid uuid NOT NULL,
 posterid uuid NOT NULL,
 parentid uuid NOT NULL,
 comment text NOT NULL
)

id是唯一的,如果该行是对现有帖子的评论,则parentid将在原始帖子上为null并指向id。插入新数据足够简单,超级快速。但是,做一个会让我回答的选择:

POST 1
COMMENT 1
COMMENT 2
POST 2
COMMENT 1
COMMENT 2

无论数据库中存在哪些行,都证明是非常困难的。我显然不能只按日期订购,因为有人可能会在帖子2发布后对帖子1发表评论。如果我执行LEFT JOIN以获取所有行上的父帖子,然后按该日期排序,则所有原始帖子组合在一起,因为它们的值为null。

然后我明白了这个想法:

CREATE TABLE wallposts
(
 id uuid NOT NULL,
 threadposted timestamp,
 posted timestamp,
 ...
 comment text
)

在原帖上,threadposted和posted会是一样的。在评论中,时间戳将是原始帖子发布的时间,“发布”将是该帖子上的评论发布的时间。现在我可以这样做:

select * from wallposts order by threadposted, posted;

这很有效,但有一件事让我感到烦恼。如果两个人同时创建一个帖子,那么这两个帖子的评论将会聚集在一起,因为它们具有相同的时间戳。我可以使用“ticks”代替日期时间,但精度仍然只有1/1000秒。我还可以在threadposted和posted上设置一个唯一的约束,这会使插入更加昂贵,但如果我在一个服务器场中有多个数据库服务器,那么碰撞的可能性仍然存在。无论如何,我几乎都是这样做的,因为这种情况发生的可能性非常小,但我想知道我是否可以吃掉我的蛋糕并且还能吃到它。主要是因为我自己的教育好奇心。

第三种解决方案是以图表的形式存储这些数据。每个节点都有一个v-left和v-right指针。我可以通过“左”命令,它将按照我需要的顺序遍历树。但是,每当有人插入评论时,我都必须重新平衡整棵树。如果网站非常繁忙,这会产生大量的行锁定和各种问题。此外,它有点极端,也会导致复制问题。所以我很快就抛开了这个想法。

我还考虑过只保存原始帖子,然后以二进制形式序列化评论,因为谁关心个人评论。这将非常快,但是如果用户想要删除他们的注释或在末尾添加新注释,我必须反序列化此数据,修改结构,然后将其序列化并更新行。如果一群人同时在同一个帖子上发表评论,我可能会有随机问题。

所以这就是我最终做的事情。我查询按输入日期排序的所有帖子。在中间件层,我遍历记录集并创建原始帖子的“堆栈”,堆栈上的每个节点指向一个链接的注释列表。当我遇到一个原始帖子时,我在堆栈上推送一个新节点,当我遇到一个注释时,我将一个节点添加到链表中。我在内存中组织这个,所以我可以遍历记录集一次并有O(n)。在我创建了墙的内存表示后,我再次遍历这个数据结构并写出HTML。这很好用,具有超快速插入和超快速选择,并且没有奇怪的行锁定问题;但是在我的表示层上它有点重,并且需要我构建一个用户墙的内存表示来移动东西,所以它的顺序正确。不过,我相信这是迄今为止我发现的最佳方法。

我以为我会和其他SQL专家核实,看看是否有更好的方法可以使用一些奇怪的JOINS或UNIONS或者仍然可以与数百万用户保持同步的东西。

4 个答案:

答案 0 :(得分:1)

我认为最好在Comment上使用带有“ParentID”的简单模型以允许嵌套注释。我不认为将日期时间用作密钥通常是一种好习惯,特别是在这种情况下,您并不真正需要,并且身份ID就足够了。这是一个可能有用的基本示例:

Post
----
ID (PK)
Timestamp
UserID (FK)
Text 

Comment
-------
ID (PK)
Timestamp
PostID (FK)
ParentCommentID (FK nullable) -- allows for nested comments
Text

答案 1 :(得分:0)

您应该查看“嵌套集”。它们允许使用单个查询非常轻松地检索层次结构。 Here是关于他们的文章

如果您使用的是SQL Server 2008,则它通过“hierarchyID”类型为其提供内置支持。

如果您没有内置支持,插入和更新会更加昂贵和复杂,但查询更快更容易。

编辑: 该死的,错过了你已经知道的部分。 (正在通过手机查询)。

答案 2 :(得分:0)

您是否希望人们能够评论其他评论,即树是否具有无限深度?

如果您只想在帖子上发帖,然后对这些帖子发表评论,那么您选择了正确的行,我相信以下SQL会满足该要求(未经测试可能是错别字)

SELECT posts.id,
       posts.posted AS posted_at,
       posts.userid AS posted_by,
       posts.posterid,
       posts.comment AS post_text,
       comments.posted AS commented_at,
       comments.userid AS commented_by,
       comments.comment AS comment_text
FROM   wallposts AS posts
LEFT OUTER JOIN wallposts AS comments ON comments.parent_id = posts.id
ORDER BY posts.posted, comments.posted

这种自连接技术只是使用表别名将表连接到自身来指定连接。

答案 3 :(得分:0)

如果我们坚持你的表设计...我认为你需要在parentid列中有一些特殊的值来将原始帖子与注释分开(如果你将该列的定义更改为可空,则可能只是NULL)。然后,自我加入将起作用。像这样:

SELECT  posts.comment as [Original Post],
comments.comment as Comment 
FROM   wallposts AS posts
LEFT OUTER JOIN wallposts AS comments 
ON posts.id=comments.parentID
WHERE posts.parentID IS NULL
ORDER BY posts.posted, comments.posted

结果集在每条评论之前显示原始帖子,并且具有正确的顺序。

(这是使用SQL Server完成的,所以我不确定它是否适用于您的环境。)