【问题标题】:Order hierarchical query for comments with parent-child relation?订购具有父子关系的评论的分层查询?
【发布时间】:2018-09-19 09:39:24
【问题描述】:

我有一个 cmets 系统,它使用 adjacency list 来存储分层数据(我相信),例如:MySql 表有列 idparent_iddate、...,其中有注释没有parent_id 是主要评论,而带有parent_id 的评论自然是回复。

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

SELECT * FROM comms WHERE parent_id IS NULL

现在,如果任何 cmets 已回复,则会为该评论显示一个类似“加载回复”的按钮,然后单击另一个调用,加载所有具有 parent_id 评论 id 的 cmets,然后递归查询加载回复的回复等。这很好用,但问题是从它们的加载顺序来看,你无法真正分辨出什么是重播。

所以我想要对它们进行排序,以便在它所属的评论下进行重播。

现在这是否可能仅来自 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 ?

如果我使用我喜欢的答案中提供的“备选方案 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>

【问题讨论】:

  • 您希望它们以什么顺序出现在页面上?时间顺序?您是否正在捕获每条评论的时间戳?如果是这样,您通常会按该列排序。
  • @IanDrake 是的,我正在捕获时间戳,我希望它们按它们所属的 cmets 排序,所以如果有人用 4 的 id 回复评论,我想回复在该评论下方。
  • 查看您的第二个查询可能会有所帮助,该查询在用户单击“加载回复”时运行。您可以更新您的问题以添加它吗?
  • @IanDrake 这是一个包含两个连接的大查询,所以我将其缩减为我认为重要的部分。
  • 您为什么不通过@pv 订购它们?订购似乎是件好事,因为它包含路径。您可以用零对@pv 进行 LPAD,以便顺序正确

标签: php mysql sql ajax recursive-query


【解决方案1】:

由于这需要递归,而 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);

从调用 show_children 开始,parent_id 为 0 将获取所有顶级 cmets(那些 parent_id = NULL 的),递归将按日期顺序显示所有回复,但也按其父项的顺序显示 (无论是 cmets 还是回复)。

您还可以添加“级别”参数来设置输出样式:

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 作为 db 接口。

使用这个例程,输入数据:

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>

【讨论】:

    【解决方案2】:

    我从事 MSSQL 工作,我非常确定 CROSS APPLY 会帮助您。我假设 MYSQL 确实有那个连接。

    例如

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

    【讨论】:

    • 快速谷歌“MySQL CROSS APPLY”显示MySQL不支持。
    【解决方案3】:

    好问题:

    你现在有一个好主意:

    我会将它存储在 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");
    

    当然,当你调用你的 cmets 时,你会按时间顺序显示它们,从最新到最旧,你的 p2 成为 p3 的主要注释。它就像一个父母和一个孩子,但你以扩展关系的方式显示它。 ...这将但是 cmets 在正确的位置....

    希望这行得通。这个我没有测试过,但是我开发了一个类似原理的数据库....

    获取主要注释的代码示例:

    $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);
    

    同cmets回复主cmets的回复

    【讨论】:

    • 当你得到另一个回复的回复时,你会继续添加表格吗?
    • 不需要另一张桌子。只需查看 3 个表的结构即可。我想你会明白的。
    • 思路是这样的,如果用户打开p1的评论框并发表评论,评论将发布在tb1中。如果用户点击p1中的回复按钮并使用评论框2进行评论,评论将发布到tb2,参考主评论(main_comment_id),以及来自p3的评论。如果用户使用评论框3进行评论,按顺序回复p3,评论将发布到tb3。 . .
    • 只使用一个表就可以了
    • 我同意,我认为最好是通过一个表格,表格结构应该具体显示哪个评论是父母,孩子等。谢谢您的反馈。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 2013-08-20
    • 2010-09-13
    相关资源
    最近更新 更多