如何在 MySQL 中按节和小节编号排序?

Posted

技术标签:

【中文标题】如何在 MySQL 中按节和小节编号排序?【英文标题】:How to order by section and subsection numbers in MySQL? 【发布时间】:2020-02-24 10:55:04 【问题描述】:

我有一张这样的桌子:

+-----+-------------------------------------+--------------------------+---------------+
| id  | parent_id |  comment_text           | date_posted              |
+-----+-------------------------------------+--------------------------+---------------+
|   1 | 0         |  1                      | 2020-01-08 20:40:00      | 
|   2 | 1         |  1.1                    | 2020-01-08 20:41:00      | 
|   3 | 0         |  2                      | 2020-01-08 20:42:00      | 
|   4 | 0         |  3                      | 2020-01-08 20:43:00      | 
|   5 | 3         |  2.1                    | 2020-01-08 20:44:00      | 
|   6 | 2         |  1.1.1                  | 2020-01-08 20:45:00      |
|   7 | 1         |  1.2                    | 2020-01-08 20:46:00      | 

我如何获得这样的订购 cmets? (ORDER BY date_posted DESC 在每个评论级别并使用一个 mysql 查询,因为我需要使用限制和偏移对它们进行分页)。

comment_text           
    3
    2
       2.1
    1
       1.2
       1.1
          1.1.1

【问题讨论】:

分页是什么意思?如果你的意思是在 MySQL 查询中,你能解释一下LIMIT 2 OFFSET ... 没有给你你想要的吗? @cmbuckley 是的,但是如何像我的示例一样订购?,我刚刚更新清楚 按comment_text desc排序 什么版本的 MySQL? 8.0 有更好的层次遍历工具。 @DeLe - 表中有多少行?层次结构有多深?我们可以假设没有循环吗? 【参考方案1】:

既然您需要所有 cmets,而不仅仅是其中的一些,我不会担心其中的 MySQL 部分,它只是 SELECT * FROM Table ORDER BY Table.date_posted DESC。您也可以使用 WHERE,但根据您的示例,您将需要所有 cmets。

此处的完整示例:IDEOne Demo

完整工作示例的输出:

3
2
    2.1
1
    1.2
    1.1
        1.1.1

php 中,只需构建一个哈希,我们在其中存储一个 cmets,如 parent_id => $commentobj...

<?php

    $comment_hash = [];

    foreach($comments as $comment) 
        if(!$comment_hash[$comment['parent_id']]) 
            $comment_hash[$comment['parent_id']] = [];
        

        $comment_hash[$comment['parent_id']][] = $comment;
    

?>

我们知道 primary cmetsparent_id 为 0,因此我们对它们进行迭代。我使用递归函数,因为 cmets 可以无限响应其他 cmets,或者根据您的说明至少响应三层。

我们的递归显示函数。

<?php

    function displayComments($comments, $comment_hash, $depth) 
        foreach($comments as $comment) 
            print(str_repeat(' ', 4 * $depth));
            print($comment['comment_text']);
            print("\n");

            displayComments($comment_hash[$comment['id']], $comment_hash, $depth + 1);
        
    

?>

在顶层(即parentid=0)cmets上运行递归显示函数...

<?php

    displayComments($comment_hash[0], $comment_hash, 0);

?>

【讨论】:

谢谢,但我需要通过一个 MySQL 查询来做到这一点 b/c 我需要像我在 PHP 上的订单一样分页它们 @Dele:嘿,感谢您的关注。你甚至在上面运行了代码吗?您的帖子说您需要像“1(换行)2(换行,缩进)3”这样的数据,而您在这里的评论是“这就是我想要的方式”。你能描述得更详细些吗? 我想使用一个 MySQL 查询来获得像我的有序示例一样的有序 cmets(每个评论级别中 DESC 的 date_posted 顺序) @Dele:我明白这一点。我的代码怎么不会产生这种情况? @DeLe:您的问题中似乎多次添加或删除了有关paging 的请求。这真的是一个完全不同的问题。您应该针对遇到的每个问题提出一个问题,这样回答者会更容易。【参考方案2】:

这不是很有效,但这得到了你想要的:

SELECT * 
FROM stuff
ORDER BY SUBSTRING_INDEX(comment_text, '.', 1), 
         SUBSTRING_INDEX(comment_text, '.', 2), 
         comment_text

【讨论】:

谢谢,但我们不应该使用comment_text,b/c 这是我的例子。在现实中,这可以是一切?【参考方案3】:

SQL

SELECT * FROM tbl
ORDER BY
CAST(SUBSTRING_INDEX(comment_text, '.', 1) AS UNSIGNED) DESC, -- Up to first dot (if any)
CASE WHEN LOCATE('.', comment_text) = 0 -- No dots
     THEN 4294967295                    -- Place higher when no dots
     WHEN CHAR_LENGTH(comment_text) - CHAR_LENGTH(REPLACE(comment_text, '.', '')) = 1
     THEN CAST(SUBSTRING_INDEX(comment_text, '.', -1) AS UNSIGNED) -- Number after dot
     ELSE CAST(SUBSTRING(SUBSTRING_INDEX(comment_text, '.', -2),   -- Between 2 dots
                         1,
                         LOCATE('.', SUBSTRING_INDEX(comment_text, '.', -2))) AS UNSIGNED)
END DESC,
CASE WHEN CHAR_LENGTH(comment_text) - CHAR_LENGTH(REPLACE(comment_text, '.', '')) < 2
     THEN 4294967295                    -- Place higher when less than 2 dots
     ELSE CAST(SUBSTRING_INDEX(comment_text, '.', -1) AS UNSIGNED) -- After second dot
END DESC,
date_posted DESC;

在线演示

Rextester 演示:https://rextester.com/MPXMV17458 (包括问题中的示例 + 另一个示例,其中包含一些重复值和大于 10 的整数)。

说明

查询假设最多可以有两个点。 有三个ORDER BY 部分(除了最后的date_posted 排序),代表x.y.z 中的三个可能整数。对于前两部分,如果没有其他内容,则选择可能的最高无符号整数 - 所以 x 将高于 x.yx.y 将高于 x.y.z (或相反,因为排序是降序)。转换为无符号整数可确保排序超过 9,因为按字母顺序排序会将 10 置于 1 和 2 之间。

【讨论】:

谢谢,但我们不应该使用 comment_text,b/c 这是我的例子。实际上,这可能是所有事情? 抱歉,不太清楚您的意思。您的意思是可能有两个以上的点,还是说该表格还有您的问题中未提及的其他列? 这意味着,您正在使用 comment_text 列进行排序,但该列是用户输入,可以是所有文本 如果该列是用户输入,建议添加一些验证以确保它符合预期的格式。或者,如果“一切”真的应该被允许,是否对其他格式的排序方式有任何期望 - 例如把不符合预期格式的东西放在最后?

以上是关于如何在 MySQL 中按节和小节编号排序?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 XML 文件中按编号获取子节点

如何在 R Markdown(bookdown)中缩进编号的部分和小节?

如何在 EntityFramework 中按动态列名排序? [复制]

21《MySQL 教程》ORDER BY 排序

如何保存迭代编号。在matlab中按下按键

如何更新账单编号栏?或在 sql server 的列中按顺序排列