如何在 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 cmets 的 parent_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.y
和 x.y
将高于 x.y.z
(或相反,因为排序是降序)。转换为无符号整数可确保排序超过 9,因为按字母顺序排序会将 10 置于 1 和 2 之间。
【讨论】:
谢谢,但我们不应该使用 comment_text,b/c 这是我的例子。实际上,这可能是所有事情? 抱歉,不太清楚您的意思。您的意思是可能有两个以上的点,还是说该表格还有您的问题中未提及的其他列? 这意味着,您正在使用 comment_text 列进行排序,但该列是用户输入,可以是所有文本 如果该列是用户输入,建议添加一些验证以确保它符合预期的格式。或者,如果“一切”真的应该被允许,是否对其他格式的排序方式有任何期望 - 例如把不符合预期格式的东西放在最后?以上是关于如何在 MySQL 中按节和小节编号排序?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 R Markdown(bookdown)中缩进编号的部分和小节?