前 MariaDB 版本中的 Row_Number() 窗口函数

Posted

技术标签:

【中文标题】前 MariaDB 版本中的 Row_Number() 窗口函数【英文标题】:Row_Number() Window Function in Former MariaDB versions 【发布时间】:2020-12-31 10:19:35 【问题描述】:

有人知道如何在不支持窗口函数的版本中编写此 SQL 查询吗?

SELECT *,
       ROW_NUMBER() OVER(PARTITION BY tr.ticketId ORDER BY tr.time DESC) AS row_num
  FROM tickets AS t
  JOIN ticket_responses AS tr
    ON tr.ticketId = t.id
 WHERE row_num = 1

我找到了mysql 5.7 Getting the row number,但我不知道如何以这种方式使用 PARTITION

【问题讨论】:

这能回答你的问题吗? MYSQL 5.7 Getting the row number 【参考方案1】:

首先:您的查询不是有效的 SQL。你可以在where子句中使用窗口函数,你需要一个子查询:

select *
from (
    select *,
        row_number() over(partition by tr.ticketid order by tr.time desc) as row_num
    from tickets as t
    join ticket_responses as tr on tr.ticketid = t.id
) t
where row_num = 1

您可以使用变量来做到这一点,但我不建议这样做。 MySQL 变量存在缺陷,并且在 MySQL 8.0 中宣布了它们未来的弃用。我会推荐一个相关的子查询:

select *,
    (
        select count(*) 
        from ticket_responses tr1 
        where tr1.ticketid = t.ticketid and tr1.time <= tr.time
    ) as row_num
from tickets as t
join ticket_responses as tr on tr.ticketid = t.id

这里有几个假设:

tickets / ticket_responses 是一对多的关系

(ticket_id, time) 的元组在表 ticket_responses 中是唯一的

性能可能会受到大型数据集的影响。 ticket_responses(ticket_id, time) 上的索引可能会有所帮助。

【讨论】:

【参考方案2】:

您可以添加一个新的迭代器变量,初始化为零,例如

SELECT id, name, ticketId, time
  FROM
  (
  SELECT @row_num := IF(@id = tr.ticketId, @row_num + 1, 1) AS row_num,
         @id := tr.ticketId, tr.*
    FROM (SELECT @id := 0, @row_num := 0, tr.*, t.name 
            FROM ticket_responses AS tr
            JOIN tickets AS t
              ON tr.ticketId = t.id
           ORDER BY ticketId, time DESC) AS tr
  ) AS tt  
  WHERE tt.row_num = 1   

其中IF(@id = tr.ticketId, @rn + 1, 1) 部分与@id := tr.ticketId 一起调节分区,同时生成迭代整数

Demo

【讨论】:

我试过这段代码,它抛出错误“重复名称'列'”,所以我将代码更改为...SELECT @row_num := IF(@id = tr.ticketId, @rn + 1, 1) AS row_num, @id := tr.ticketId, t.*, tr.author as lastResponseAuthor, tr.id as lastResponseId, tr.type as lastResponseType, tr.time as lastResponseTime,结果是i.imgur.com/9aUOTNQ.png,但我需要i.imgur.com/AJ2ejrd.png我的错,我应该在开始时发送这些屏幕。 现在它应该返回 4 行,但最后一个响应不是最后一个响应,也许我只是写错了,但没关系,我找到了这个解决方案:SELECT * from tickets as t LEFT JOIN (SELECT ticketId, max(time) as time FROM ticket_responses GROUP BY ticketId) as last_responses ON t.id = last_responses.ticketId LEFT JOIN ticket_responses as tr ON t.id = tr.ticketId AND last_responses.time = tr.time; 并且它正在工作也。感谢您的帮助。【参考方案3】:

您的查询无效,因为没有为 where 子句定义 row_num。在 MariaDB 中,您需要一个子查询或 CTE。让我假设您过度简化了查询,并且您想要 ticket_response 和最新的 time

我会建议一个相关的子查询:

SELECT *
FROM tickets t JOIN
     ticket_responses tr
     ON tr.ticketId = t.id
WHERE tr.time = (SELECT MIN(tr2.time)
                 FROM ticket_responses tr2
                 WHERE tr2.ticketId = tr.ticketId
                );

注意:这并不完全相同。它实际上等价于rank() -- 但如果time 是唯一的,则返回相同的结果。

如果time 可能不是唯一的,那么您需要一些方法来按时分离重复项。如果我假设ticket_responses 中有一个唯一的 id,那么:

SELECT *
FROM tickets t JOIN
     ticket_responses tr
     ON tr.ticketId = t.id
WHERE tr.ticket_responses_id = (SELECT tr2.ticket_responses_id
                                FROM ticket_responses tr2
                                WHERE tr2.ticketId = tr.ticketId
                                ORDER BY tr2.time DESC, tr2.ticket_responses_id DESC
                                LIMIT 1
                               );

【讨论】:

以上是关于前 MariaDB 版本中的 Row_Number() 窗口函数的主要内容,如果未能解决你的问题,请参考以下文章

在 MariaDB 中使用 ROW_NUMBER() 函数的问题

MariaDB ROW_NUMBER 与 ORDER BY 未正确排序

ROW_NUMBER()在查询业绩排名中的使用

如果不使用 sqlite 中的 row_number() 函数,如何提供相同的函数?

模拟 row_number 函数

sql 分组取最新的数据sqlserver巧用row_number和partition by分组取top数据