评论和评论回复的mysql结构

Posted

技术标签:

【中文标题】评论和评论回复的mysql结构【英文标题】:mysql structure for comments and comment replies 【发布时间】:2009-08-11 14:53:20 【问题描述】:

我已经考虑这个问题已经有一段时间了,我需要一种方法来在数据库中添加对 cme​​ts 的回复,但我不知道如何继续。

这是我目前的评论表(不多说但它是一个开始):

CREATE TABLE IF NOT EXISTS `comments` (
  `id` int(12) NOT NULL AUTO_INCREMENT,
  `comment` text,
  `user_id` int(12) DEFAULT NULL,
  `topic_id` int(12) NOT NULL,
  `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  KEY `topic_id` (`topic_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=27 ;

这是我当前的查询:

SELECT c.id, c.comment, c.user_id, u.username, u.photo
FROM (comments c)
JOIN users u ON c.user_id = u.id
WHERE c.topic_id = 9

一种选择是创建一个名为“comment_replies”的新表,但我不确定是否能够在一个查询中选择所有 cmets 和评论回复,以及是否添加一个名为“reply”的新列我不知道如何对它们进行排序以在每个回复中获得每条评论。

我很想就如何处理这个问题获得一些建议。

编辑:

按照以下关于添加 parent_comment_id 的答案从 1 条评论和 2 条回复中产生这种数组:

array(2) 
  [0]=>
  object(stdClass)#17 (7) 
    ["id"]=>
    string(2) "26"
    ["comment"]=>
    string(36) "adding a comment from the admin page"
    ["user_id"]=>
    string(2) "16"
    ["ts"]=>
    string(10) "1249869350"
    ["username"]=>
    string(5) "Admin"
    ["photo"]=>
    string(13) "gravatar2.png"
    ["reply"]=>
    string(23) "There is no admin page!"
  
  [1]=>
  object(stdClass)#18 (7) 
    ["id"]=>
    string(2) "26"
    ["comment"]=>
    string(36) "adding a comment from the admin page"
    ["user_id"]=>
    string(2) "16"
    ["ts"]=>
    string(10) "1249869350"
    ["username"]=>
    string(5) "Admin"
    ["photo"]=>
    string(13) "gravatar2.png"
    ["reply"]=>
    string(13) "Yes there is!"
  

我应该如何处理这个数组以使用它,是否可以将评论与回复分开?

【问题讨论】:

【参考方案1】:

如果您希望人们能够回复回复(即具有回复的层次结构,例如您在在线消息论坛中看到的),那么我将在 cmets 表中添加一个可选的 parent_comment_id 字段。

你的桌子应该是这样的

`CREATE TABLE IF NOT EXISTS `comments` (
  `id` int(12) NOT NULL AUTO_INCREMENT,
  `parent_comment_id` int(12) NULL,
  `comment` text,
  `user_id` int(12) DEFAULT NULL,
  `topic_id` int(12) NOT NULL,
  `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  KEY `topic_id` (`topic_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=27 ;`

显示所有 cmets 和回复的查询类似于:

SELECT c.id, c.comment, r.comment as reply, c.user_id, u.username, u.photo
FROM (comments c)
JOIN users u ON c.user_id = u.id
LEFT JOIN comments r ON c.id = r.parent_comment_id
WHERE c.topic_id = 9

但是请注意,使用此查询,您的回复不仅会显示在“回复”列中,还会显示在“评论”列中,作为附加行,每行都有零个或多个回复。

要显示回复评论的用户的用户名,您需要两次加入 users 表(第一次是发布原始评论的用户,第二次是回复的用户)。试试这个查询来显示回复用户的用户名:

SELECT c.id, c.comment, c.user_id, u.username, u.photo, r.comment as reply, r.user_id as reply_user_id, 
u2.username as reply_username, u2.photo as reply_photo
FROM (comment c)
JOIN users u ON c.user_id = u.id
LEFT JOIN comments r ON c.id = r.parent_comment_id
JOIN users u2 ON r.user_id = u2.id
WHERE c.topic_id = 9

【讨论】:

我添加了 parent_comment_id 并更改了查询,但我不确定下一步该做什么。假设我有 1 条评论和 2 条回复,这将生成 2 个 cmets,两者将具有相同的评论但回复不同,并且两个回复都将具有第一个 cmets 用户名。我用一个例子编辑了我的主要帖子。 我已经编辑了上面的原始查询,以便显示回复评论的用户的用户名和照片。 看起来不错,但它仍然会输出重复的评论值,如果您对一条评论有多个回复,我是否必须使用服务器端代码更改结果数组以将 cmets 与回复?【参考方案2】:

parent_comment_id 列添加到您的 cmets 表。使其成为可选的。查询时,可以将所有子 cmets 加入每个父节点。作为一些选择性的非规范化(轻微冗余),您可以确保在子 cmets 上也设置了 topic_id,让您更轻松地拉动它们(假设您要在主评论线程中显示所有子 cmets而不是通过较小的 ajax 请求)。

根据需要构建演示文稿,将结果放入 memcached(或平面文件或内存......但您正在缓存),然后您就设置好了。

【讨论】:

【参考方案3】:

我决定在数据库中添加 parent_id 列,而不是左加入回复,我只是一次选择了所有 cmets,稍后使用服务器端代码对 cme​​ts 和回复进行排序,查询如下:

SELECT c.*, u.username, u.photo
FROM (comments c)
JOIN users u ON c.user_id = u.id
WHERE c.topic_id = 9
ORDER BY c.id ASC

现在我将查询结果传递给下面的函数,这样每个回复都会作为一个数组添加到评论数组中,所以基本上它返回一个多维数组。

function sort_comments($ar)

    $comments = array();
    foreach($ar as $item)
    
        if(is_null($item['parent_id'])) $comments[] = $item;
        else 
        
            $parent_array = array_search_key($item['parent_id'],$comments,'id');
            if($parent_array !== false) $comments[$parent_array]['replies'][] = $item;
        
    
    return $comments;

【讨论】:

什么是函数array_search_key - 它不是 php 标准库函数 array_search_key 函数在哪里??它存在于 php 中吗?【参考方案4】:

这看起来一切都很好,但是如果表包含超过一百万行呢?有些 cmets 可能相隔数十万行。这些查询将如何执行?

【讨论】:

是的,但现在不太可能,我认为最好的方法是将它们分成两个不同的表,如果你有这么多的数据要处理。【参考方案5】:

评论回复是具有父comment_id 的评论。尝试将 comment_id 作为字段添加到您的 cmets 表中。你会得到一个树状结构。

如果您想检索整个 cmets 树,最好的办法是使用嵌套集 (https://wiki.htc.lan/Hierarchy_model)。但这是一个更复杂的解决方案。

这里有一些来自 mysql 的更多信息:http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

【讨论】:

【参考方案6】:

看起来您正在使用 WordPress,添加 parent_comment_id 本来是一个理想的解决方案,但在这种情况下并非如此。

首先,我认为修改 WordPress 基本表不是一个好主意。其次,您最终会得到一个复杂的代码,该代码会因 wordpress 更新而中断。

最好使用像Intense Comments这样的插件

如果您想创建自己的解决方案,我会说创建另一个表格来回复评论。 a) 你的新表应该是这样的

`CREATE TABLE IF NOT EXISTS `comment_replies` (
  `id` int(12) NOT NULL AUTO_INCREMENT,
  `parent_comment_id` int(12) NULL,
  `comment` text,
  `user_id` int(12) DEFAULT NULL,
  `topic_id` int(12) NOT NULL,
  `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  KEY `topic_id` (`topic_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8

b) 你会这样获取它们

$comment_ids = array_map( 'intval', array_keys( $comments ) );
sort( $comment_ids );
$comments_id_list = join( ',', $comment_ids );

$query = "SELECT c.id, c.comment, c.user_id, u.username, u.photo
FROM (comment_replies c)
JOIN users u ON c.user_id = u.id
WHERE c.parent_comment_id IN ( $comments_id_list )"

$replies = $wpdb->get_results($query);
$replies_by_parent = array();

foreach ( array_keys( $replies ) as $i )           
    $replies_by_parent [$replies[$i]->id] = array();

foreach ( array_keys( $replies ) as $i )           
    $replies_by_parent [$replies[$i]->id][] =& $replies[$i];

c) 现在在您的 cmets 循环中,您可以获得这样的回复

foreach (  $replies_by_parent [$parent_id] as $reply )             
        echo $reply->comment;
    

【讨论】:

我不使用wordpress,但解决方案看起来相当不错。我必须更多地研究它,看看它是否最适合我。【参考方案7】:
function sort_comments($ar)

    $comments = [];
    $i=0;
    foreach($ar as $co)
      if(!empty($co['comment_replyof'])) 
        $comments[$co['comment_replyof']]['replies'] = $co;
      else
        foreach($co as $c => $o) $comments[$co['comment_id']][$c] = $o;
      
    $i++;
    
    return $comments;


SELECT C.*, U.id,U.fname, U.lname FROM (comment C) JOIN users U ON `enter code here`C.comment_user = U.id where C.comment_content='10' 

【讨论】:

以上是关于评论和评论回复的mysql结构的主要内容,如果未能解决你的问题,请参考以下文章

MySql vs NoSql - 社交网络评论和通知数据结构和实现

Mysql - 正确排序 Facebook 帖子、评论和回复

评论系统的实施(Facebook赞)

Mongo 聚合计数子文档(计数回复评论)

使用单个 SQL 查询选择所有带有父评论的回复

PHP/MySql 选择与回复分组的评论