订购具有父子关系的评论的分层查询?

Order hierarchical query for comments with parent-child relation?

我有一个使用 adjacency list 存储分层数据的评论系统(我相信),例如:MySql table 有列 idparent_id, date, ... ,其中没有 parent_id 的评论是主要评论,带有 parent_id 的评论自然是回复。

在初始页面加载时,我进行了一个 Ajax 调用,加载了所有没有 parent_id 的评论,因此所有主要评论。

SELECT * FROM comms WHERE parent_id IS NULL

现在,如果任何评论有回复,该评论会出现 "load replies" 之类的按钮,点击后会进行另一个调用,加载所有 parent_id 评论 [=13] 的评论=],然后递归查询加载回复的回复等等。这很好用,问题是从它们的加载顺序来看,你无法真正分辨出什么是重播。

所以我想要的是订购它们,以便重播在它所属的评论下。

现在只有 sql 做类似 ORDER BY id = parent_id 的事情才有可能,对它们进行排序以便它们有点意义,还是我应该从 php 处理这个问题?或者我应该重新开始并以不同的方式存储它们?

编辑:第二个查询的一部分(示例取自 this 我不久前找到的答案)

SELECT date_p, parent_id, id
   FROM (SELECT * FROM comms) rec,
   (SELECT @pv := 14) initialisation
   WHERE find_in_set(parent_id, @pv) > 0 
   AND @pv := concat(@pv, ',', id) ORDER BY ?

如果我使用我喜欢的答案中提供的"Alternative 1",订购方法会有所不同还是更好?

这就是我想要达到的目标:

<p>Main comm 1</p>
  <p>reply to main comm 1</p>
  <p>another reply to main comm 1</p>
    <p> replay to reply of main comm 1</p>
  <p> yet another reply to main comm 1</p>
<p>Main comm 2</p>
<p>Main comm 3</p>

好问题:

你现在的想法很棒:

我会把它存储在 3 个不同的表中,

tb1) 主要评论 tb2) 回复主要评论 tb3) 回复主要评论的回复。

tb1 包含像 id (auto_increment) 这样的列成为您的主要评论 id

tb2) 包含像 id(auto_increment)、main_comment_id、reply_to_main_comment_id

这样的列

tb3) 有 id(auto_increment), main_comment_id, reply_to_main_comment_id, reply_reply_id

<p1>main_comment_id</p1>
  <p2>reply_to_main_comment_id</p2>
     <p3>reply_reply_id</p3>

logically like the following:

    if(p1){
       post to tb1
    }elseif(p2){
       post to tb2
    }elseif(p3){
       post to tb3
    }


To get the comments for p1, (Select comments from tb1)
To get comments for p2, (Select comments from tb2 where main_comment_id=$id)
To get comments for p3, (Select comments from tb3 where main_comment_id=$id 
 and reply_to_main_comment_id=$id2)

$sql ("SELECT comments from tb3 WHERE main_comment_id=$id1 AND reply_to_main_comment_id=$id2 ORDER BY date DESC");

当你调用你的评论时,当然你会按时间顺序显示它们,从最新到最旧,你的p2成为p3的主要评论..它就像父子一样,但你以扩展关系显示它方式....这将但评论在正确的地方....

希望这能奏效。我没有测试过这个,但我开发了一个具有类似原理的数据库....

获得主要评论的代码示例:

$comment1 = $_POST['comment1'];
$comment1_sql = ("Insert INTO tb1 (main_comment) VALUES ($comment1)");
$comment_result = mysqli_query($conn, $comment_sql1);

代码示例回复主评论:

$comment2 = $_POST['comment2'];
$comment2_sql = ("Insert INTO tb2 (reply_to_main_comment) VALUES ($comment2)");
$comment_result = mysqli_query($conn, $comment_sql2);

同评论回复主评论的回复

由于这需要递归,而MySQL pre v8 并不真正支持它,我倾向于在擅长递归的PHP 中解决问题。基本套路很简单:

function show_children($db, $parent) {
    $sql = "SELECT * FROM comms WHERE parent_id " . ($parent ? "= $parent" : "IS NULL") . " ORDER BY date_p ASC";
    $result = $db->query($sql);
    if (!$result) return;
    while ($row = $result->fetch_assoc()) {
        echo "<p>" . $row['cmt'] . "</p>";
        show_children($db, $row['id']);
    }
}

show_children($db, 0);

以 parent_id 为 0 调用 show_children 开始,将获取所有 top-level 评论(parent_id = NULL 的评论),递归将显示所有回复按日期顺序排列,但也按 parents 的顺序排列(无论是评论还是回复)。

您还可以添加一个 "level" 参数来设置输出样式:

function show_children($db, $parent, $level = 0) {
    $sql = "SELECT * FROM comms WHERE parent_id " . ($parent ? "= $parent" : "IS NULL") . " ORDER BY date_p ASC";
    $result = $db->query($sql);
    if (!$result) return;
    while ($row = $result->fetch_assoc()) {
        echo "<p class=\"level$level\">" . $row['cmt'] . "</p>";
        show_children($db, $row['id'], $level+1);
    }
}

show_children($db, 0);

注意我假设 mysqli 作为数据库接口。

使用此例程,输入数据:

id  date_p      parent_id   cmt
1   2018-03-21  (null)      Main comm1
2   2018-03-22  (null)      Main comm2
3   2018-03-22  1           reply to main comm 1
4   2018-03-23  1           another reply to main comm 1
5   2018-03-23  1           yet another reply to main comm 1
6   2018-03-24  4           replay to reply of main comm 1
7   2018-03-24  (null)      Main comm3

你会得到这个输出:

<p class="level0">Main comm1</p>
<p class="level1">reply to main comm 1</p>
<p class="level1">another reply to main comm 1</p>
<p class="level2"> replay to reply of main comm 1</p>
<p class="level1"> yet another reply to main comm 1</p>
<p class="level0">Main comm2</p>
<p class="level0">Main comm3</p>

我在 MSSQL 上工作,我非常确定 CROSS APPLY 会对您有所帮助。我假设 MYSQL 确实有 join .

例如

SELECT A.PARENT_ID,A.COMMENT,* FROM T A 
CROSS APPLY T B 
where a.id = 3