Select 如果 RANK()+1 因 JOINS 溢出而排名第一

Select first rank if RANK()+1 overflows with JOINS

我有一个名为 loop_msg 的 table 以及 msg_idcontent。我有第二个 table,名为 loop_msg_statuschannelmsg_id。这用于 post 消息在不同频道中作为循环,所以我需要跟踪在每个 channel 中哪个 msg_id 最后被 posted。

SELECT 
  a.msg_id, 
  b.content, 
  b.rank, 
  b.rank + 1, 
  c.rank, 
  c.content as next_content 
FROM 
  loop_msg_status as a 
  LEFT JOIN (
    SELECT 
      *, 
      RANK() OVER (
        ORDER BY 
          msg_id ASC
      ) as rank 
    FROM 
      loop_msg
  ) b ON a.msg_id = b.msg_id 
  LEFT JOIN (
    SELECT 
      *, 
      RANK() OVER (
        ORDER BY 
          msg_id ASC
      ) as rank 
    FROM 
      loop_msg
  ) c ON b.rank + 1 = c.rank

通过这个查询,我能够从每个 channel 中获取当前的 msg_id 及其 content。我还从 table loop_msg 得到 msg_idrank。我也得到它的 rank+1rank+1content,如果这有意义的话。它有效。但是,如果 rank 是最高的,那么 rank+1 不存在,我得到 NULL next_content。在这种情况下,我想 SELECT 来自 loop_msg 的最低 rank,即 1,并将其内容改为 next_content。我应该添加 IF() 吗?如果是,在哪里?或者有更好的方法吗?

SELECT version();
> 10.5.13-MariaDB

完整 SQL 示例:

CREATE TABLE `loop_msg` (
  `msg_id` int(11) NOT NULL,
  `content` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `loop_msg` (`msg_id`, `content`) VALUES
(2, 'Content 2'),
(3, 'Content 3'),
(4, 'Content 4'),
(6, 'Content 6'),
(7, 'Content 7'),
(8, 'Content 8');

CREATE TABLE `loop_msg_status` (
  `channel` bigint(20) NOT NULL,
  `msg_id` int(11) NOT NULL DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `loop_msg_status` (`channel`, `msg_id`) VALUES
(316757642527768577, 4),
(384071823261696010, 6),
(939746456632438804, 8);


ALTER TABLE `loop_msg`
  ADD PRIMARY KEY (`msg_id`);

ALTER TABLE `loop_msg_status`
  ADD PRIMARY KEY (`channel`);


ALTER TABLE `loop_msg`
  MODIFY `msg_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=9;
COMMIT;

预期结果

channel            | nextContent
--------------------------------
316757642527768577 | Content 6
384071823261696010 | Content 7
939746456632438804 | Content 2

最终 SQL 问题中给出的架构(和预期结果):

The final fiddle

WITH cte AS (
        SELECT loop_msg.msg_id
             , channel
             , COALESCE(
                           LEAD(content)        OVER w1
                         , FIRST_VALUE(content) OVER w1
                       ) AS content_next
          FROM      loop_msg
          LEFT JOIN loop_msg_status
            ON loop_msg.msg_id = loop_msg_status.msg_id
          WINDOW w1 AS (ORDER BY loop_msg.msg_id)
     )
SELECT channel, content_next
  FROM cte
 WHERE channel IS NOT NULL
;

结果:

+--------------------+--------------+
| channel            | content_next |
+--------------------+--------------+
| 316757642527768577 | Content 6    |
| 384071823261696010 | Content 7    |
| 939746456632438804 | Content 2    |
+--------------------+--------------+

要同时查看当前和下一个 msg_id,这里是调整后的 SQL:

The adjusted fiddle

WITH cte AS (
        SELECT loop_msg.msg_id
             , channel
             , ROW_NUMBER() OVER w1 as rankx
             , COALESCE(
                           LEAD(loop_msg.msg_id)        OVER w1
                         , FIRST_VALUE(loop_msg.msg_id) OVER w1
                       ) AS msgid_next
             , COALESCE(
                           LEAD(content)        OVER w1
                         , FIRST_VALUE(content) OVER w1
                       ) AS content_next
          FROM      loop_msg
          LEFT JOIN loop_msg_status
            ON loop_msg.msg_id = loop_msg_status.msg_id
          WINDOW w1 AS (ORDER BY loop_msg.msg_id)
     )
SELECT channel, content_next, msg_id, msgid_next
  FROM cte
 WHERE channel IS NOT NULL
;

结果:

+--------------------+--------------+--------+------------+
| channel            | content_next | msg_id | msgid_next |
+--------------------+--------------+--------+------------+
| 316757642527768577 | Content 6    |      4 |          6 |
| 384071823261696010 | Content 7    |      6 |          7 |
| 939746456632438804 | Content 2    |      8 |          2 |
+--------------------+--------------+--------+------------+

更多详情:

你可以尝试这样的事情。在这种情况下,我们可以使用 ROW_NUMBER 而不是 RANK,因为 msg_id 是唯一的,并且没有两条消息具有相同的 msg_id 而具有相同的 RANK。如果您愿意,可以随意替换为 RANK。

The fiddle

稍后,我们可以按频道分区,分别为每个频道执行此操作。您的问题并未完全清楚您希望如何使用频道。

SELECT *
     , ROW_NUMBER() OVER (ORDER BY msg_id ASC) as rankx
     , COALESCE(
                   LEAD(msg_id)        OVER (ORDER BY msg_id ASC)
                 , FIRST_VALUE(msg_id) OVER (ORDER BY msg_id ASC)
               ) AS msgid_next
     , COALESCE(
                   LEAD(content)        OVER (ORDER BY msg_id ASC)
                 , FIRST_VALUE(content) OVER (ORDER BY msg_id ASC)
               ) AS content_next
  FROM loop_msg
;

结果:

+--------+---------+-------+------------+--------------+
| msg_id | content | rankx | msgid_next | content_next |
+--------+---------+-------+------------+--------------+
|      1 | c1      |     1 |          2 | c2           |
|      2 | c2      |     2 |          3 | c3           |
|      3 | c3      |     3 |          4 | c4           |
|      4 | c4      |     4 |          5 | c5           |
|      5 | c5      |     5 |          6 | c6           |
|      6 | c6      |     6 |          7 | c7           |
|      7 | c7      |     7 |          1 | c1           |
+--------+---------+-------+------------+--------------+

设置:

CREATE TABLE loop_msg (
     msg_id  int  auto_increment primary key
   , content varchar(20)
);

INSERT INTO loop_msg (content) VALUES
   ('c1'), ('c2'), ('c3'), ('c4'), ('c5'), ('c6'), ('c7')
;

测试 #2,每个通道处理:

CREATE TABLE loop_msg (
      msg_id  int  auto_increment primary key
    , chan    varchar(20)
    , content varchar(20)
);

INSERT INTO loop_msg (content, chan) VALUES
   ('c1', 'chan1')
 , ('c2', 'chan1')
 , ('c3', 'chan1')
 , ('c4', 'chan1')
 , ('c5', 'chan1')
 , ('c6', 'chan1')
 , ('c7', 'chan1')
 , ('d2', 'chan2')
 , ('d3', 'chan2')
 , ('d4', 'chan2')
 , ('d5', 'chan2')
 , ('d6', 'chan2')
 , ('d7', 'chan2')
 , ('d8', 'chan2')
;

SELECT *
     , ROW_NUMBER() OVER (PARTITION BY chan ORDER BY msg_id ASC) as rankx
     , COALESCE(
                   LEAD(msg_id)        OVER (PARTITION BY chan ORDER BY msg_id)
                 , FIRST_VALUE(msg_id) OVER (PARTITION BY chan ORDER BY msg_id)
               ) AS msgid_next
     , COALESCE(
                   LEAD(content)        OVER (PARTITION BY chan ORDER BY msg_id)
                 , FIRST_VALUE(content) OVER (PARTITION BY chan ORDER BY msg_id)
               ) AS content_next
  FROM loop_msg
;

结果:

+--------+-------+---------+-------+------------+--------------+
| msg_id | chan  | content | rankx | msgid_next | content_next |
+--------+-------+---------+-------+------------+--------------+
|      1 | chan1 | c1      |     1 |          2 | c2           |
|      2 | chan1 | c2      |     2 |          3 | c3           |
|      3 | chan1 | c3      |     3 |          4 | c4           |
|      4 | chan1 | c4      |     4 |          5 | c5           |
|      5 | chan1 | c5      |     5 |          6 | c6           |
|      6 | chan1 | c6      |     6 |          7 | c7           |
|      7 | chan1 | c7      |     7 |          1 | c1           |
|      8 | chan2 | d2      |     1 |          9 | d3           |
|      9 | chan2 | d3      |     2 |         10 | d4           |
|     10 | chan2 | d4      |     3 |         11 | d5           |
|     11 | chan2 | d5      |     4 |         12 | d6           |
|     12 | chan2 | d6      |     5 |         13 | d7           |
|     13 | chan2 | d7      |     6 |         14 | d8           |
|     14 | chan2 | d8      |     7 |          8 | d2           |
+--------+-------+---------+-------+------------+--------------+

最后:

我们也可以定义一个window子句来避免每次都重写规范:

SELECT *
     , ROW_NUMBER() OVER w1 as rankx
     , COALESCE(
                   LEAD(msg_id)        OVER w1
                 , FIRST_VALUE(msg_id) OVER w1
               ) AS msgid_next
     , COALESCE(
                   LEAD(content)        OVER w1
                 , FIRST_VALUE(content) OVER w1
               ) AS content_next
  FROM loop_msg
  WINDOW w1 AS (PARTITION BY chan ORDER BY msg_id)
;

结果:

+--------+-------+---------+-------+------------+--------------+
| msg_id | chan  | content | rankx | msgid_next | content_next |
+--------+-------+---------+-------+------------+--------------+
|      1 | chan1 | c1      |     1 |          2 | c2           |
|      2 | chan1 | c2      |     2 |          3 | c3           |
|      3 | chan1 | c3      |     3 |          4 | c4           |
|      4 | chan1 | c4      |     4 |          5 | c5           |
|      5 | chan1 | c5      |     5 |          6 | c6           |
|      6 | chan1 | c6      |     6 |          7 | c7           |
|      7 | chan1 | c7      |     7 |          1 | c1           |
|      8 | chan2 | d2      |     1 |          9 | d3           |
|      9 | chan2 | d3      |     2 |         10 | d4           |
|     10 | chan2 | d4      |     3 |         11 | d5           |
|     11 | chan2 | d5      |     4 |         12 | d6           |
|     12 | chan2 | d6      |     5 |         13 | d7           |
|     13 | chan2 | d7      |     6 |         14 | d8           |
|     14 | chan2 | d8      |     7 |          8 | d2           |
+--------+-------+---------+-------+------------+--------------+