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 两个具有优先级的字段,而不是一个接一个的主要内容,如果未能解决你的问题,请参考以下文章

SQL:ORDER BY 两列混合,不基于优先级

若sql语句中order by指定了多个字段,则怎么排序?

若sql语句中order by指定了多个字段,则怎么排序?

sql中distinct和order by问题的解决方案

SQL基础-order by

SQL语句order by两个字段同时排序