SQL ORDER BY 两个具有优先级的字段,而不是一个接一个
Posted
技术标签:
【中文标题】SQL ORDER BY 两个具有优先级的字段,而不是一个接一个【英文标题】:SQL ORDER BY two fields with priority, rather than one after the other 【发布时间】:2021-06-07 19:37:58 【问题描述】:用于留言板系统。有一般帖子,以及对这些帖子的直接回复。
SELECT * FROM msgs ORDER BY msg_id ASC, msg_replyto ASC
不会给我想要的结果,因为它列出了所有的一般帖子,然后是所有的回复。我不知道该怎么表达我的要求,所以我所能做的就是给你模型表和数据。
这是示例表结构和行:
msg_id | msg_text | msg_replyto
----------------------------------------------------------------
1 | This is a post ID #1 | NULL
2 | This is a post ID #2 | NULL
3 | This is a direct reply to post ID #1 | 1
4 | This is a post ID #4 | NULL
5 | This is a post ID #5 | NULL
6 | This is a direct reply to post ID #4 | 4
7 | This is a post ID #7 | NULL
8 | This is a direct reply to post ID #3, | 3
| which is a direct reply to post ID #1. |
我尝试返回行的正确顺序如下:
msg_id | msg_replyto
-----------------------
1 | NULL
3 | 1
8 | 3
2 | NULL
4 | NULL
6 | 4
5 | NULL
7 | NULL
其中,首先 msg_id 具有优先级 - 除非一行中的字段 msg_replyto 等于另一行的 msg_id,然后 msg_replyto 具有次要优先级。如果这没有意义,我很抱歉,我没有其他方法可以解释它。我试着搜索这个,但什至不知道如何措辞。
这是创建示例的实际 SQL。
CREATE TABLE IF NOT EXISTS `msgs` (
`msg_id` int(11) NOT NULL AUTO_INCREMENT,
`msg_text` varchar(200) NOT NULL,
`msg_replyto` int(11) DEFAULT NULL,
PRIMARY KEY (`msg_id`)
) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
INSERT INTO `msgs` (`msg_id`, `msg_text`, `msg_replyto`) VALUES
(1, 'This is a post ID #1', NULL),
(2, 'This is a post ID #2', NULL),
(3, 'This is a direct reply to post ID #1', 1),
(4, 'This is a post ID #4', NULL),
(5, 'This is a post ID #5', NULL),
(6, 'This is a direct reply to post ID #4', 4),
(7, 'This is a post ID #7 ', NULL),
(8, 'This is a direct reply to post ID #3, which is a direct reply to post ID #1', 3);
COMMIT;
【问题讨论】:
你的mysql是什么版本的? MYSQL 版本 5.7.31。我还安装了 8 个(我在笔记本电脑上使用 WAMPServer 作为 localhost) 我为示例表和数据集添加了 SQL。除此之外,我看不出我是怎么不遵守的。 【参考方案1】:作为一个普遍问题,没有简单的方法可以解决这个问题。你有一个图表行走问题。
但是有办法。您可以将每个方法的路径构造为字符串,然后按该字符串排序:
with recursive cte as (
select msg_id, cast(lpad(msg_id, 5, '0') as char(10000)) as path
from msgs
where msg_replyto is null
union all
select m.msg_id, concat_ws('->', path, lpad(m.msg_id, 5, '0'))
from cte join
msgs m
on m.msg_replyto = cte.msg_id
)
select m.*
from msgs m left join
cte
on m.msg_id = cte.msg_id
order by cte.path;
Here 是一个 dbfiddle。
【讨论】:
哇,谢谢。适用于 8,在 5.7.x 上引发错误。有没有更简单的方法来实现这一点,例如为直接回复创建一个完整的其他表 (msg_replyto) 并创建一个 UNION 或其他东西? @EasterEggs 。 . . MySQL 从版本 8 开始支持递归子查询。在没有存储过程的早期版本中,实际上没有办法解决这个问题。【参考方案2】:下面应该得到你正在寻找的东西。它使用两个自连接来连接 msg_replyto 值,然后按 msg_id 和 msg_replyto 排序
SELECT C.*
FROM msgs AS A
LEFT JOIN msgs AS B
ON A.msg_id = IFNULL(B.msg_replyto,B.msg_id)
JOIN msgs AS C
ON B.msg_id = IFNULL(C.msg_replyto,C.msg_id)
ORDER BY A.msg_id
,B.msg_id
结果
msg_id | msg_text | msg_replyto |
---|---|---|
1 | This is a post ID #1 | NULL |
3 | This is a direct reply to post ID #1 | 1 |
8 | This is a direct reply to post ID #3, which is a direct reply to post ID #1 | 3 |
2 | This is a post ID #2 | NULL |
4 | This is a post ID #4 | NULL |
6 | This is a direct reply to post ID #4 | 4 |
5 | This is a post ID #5 | NULL |
7 | This is a post ID #7 | NULL |
【讨论】:
这有点工作,但是直接回复其他直接回复的消息是乱序的。这将按 1,3,2,8,4,6,5,7 而不是 1,3,8,2,4,6,5,7 的顺序返回行 @EasterEggs - 我刚刚修改了代码,上面应该可以满足您的需要。 太棒了,谢谢先生。我开始认为我只需要修改显示结果的 php 代码,这在我的情况下是不可取的。 @EasterEggs - 很高兴我能帮上忙 :) 如果它适合您的需要,请将我的回复标记为答案。如果您还有其他问题,请随时提出。以上是关于SQL ORDER BY 两个具有优先级的字段,而不是一个接一个的主要内容,如果未能解决你的问题,请参考以下文章