MYSql 窗口函数 - 获取最后一个值

Posted

技术标签:

【中文标题】MYSql 窗口函数 - 获取最后一个值【英文标题】:MYSql window function - get last value 【发布时间】:2020-02-23 09:45:16 【问题描述】:

我正在尝试获取某个时间窗口内玩家余额的最后一个值。

我有一个transactions 表。

玩家余额不是最大值也不是最小值。

SELECT  project_id, 
        player_id,
        FIRST_VALUE(balance) OVER (PARTITION BY player_id ORDER BY event_arrival_time DESC) AS balance
FROM transactions
WHERE event_arrival_time BETWEEN '2019-12-02 00:00:00' AND '2019-12-03 23:59:59'
        AND project_id='aaa' 
GROUP BY project_id, player_id

我得到了值,但如果我使用对单个玩家发出的查询来测试它们,我会得到不同的平衡,我会在周期中间的某个地方看到给定的结果平衡。

另外,如果我多次运行这个查询,我会得到不同的余额,就像它选择不同的交易一样(我们谈论的是 10 分钟的差异)。

SELECT * 
FROM transacitions
where event_arrival_time BETWEEN '2019-12-02 00:00:00' AND '2019-12-03 23:59:59'
        AND project_id='aaa' and player_id = 'player1'
ORDER BY event_arrival_time desc

我想获取那个时期的玩家列表,以及他们的最新余额(不是 MAX 值 - 可能是最大日期)。

【问题讨论】:

【参考方案1】:

删除GROUP BY 子句,如果需要,在SELECT 中使用DISTINCT

SELECT DISTINCT 
  project_id, 
  player_id,
  FIRST_VALUE(balance) OVER (PARTITION BY player_id ORDER BY event_arrival_time DESC) AS balance
FROM transactions
WHERE event_arrival_time BETWEEN '2019-12-02 00:00:00' AND '2019-12-03 23:59:59'
  AND project_id='aaa'

【讨论】:

【参考方案2】:

您需要过滤,而不是聚合。

您可以使用相关子查询来做到这一点:

SELECT project_id, player_id, balance
FROM transactions t
WHERE event_arrival_time = (
    SELECT MAX(t1. event_arrival_time)
    FROM transactions t1
    WHERE 
        t1.player_id = t.player_id
        AND t1.event_arrival_time >= '2019-12-02'
        AND t1.event_arrival_time < '2019-12-03'
        AND t1.project_id = 'aaa' 
    )

为了提高性能,您需要在(project_id, player_id, event_arrival_time) 上建立索引。您也可以尝试覆盖索引:(project_id, player_id, event_arrival_time, balance);有了这样的索引,数据库可能只通过查看索引来执行整个查询,而无需实际访问底层数据。

你也可以使用窗口函数:


SELECT project_id, player_id, balance
FROM (
    SELECT 
        t.*,
        RANK() OVER(PARTITION BY player_id ORDER BY event_arrival_time DESC) rn
    FROM transactions t
    WHERE
        event_arrival_time >= '2019-12-02'
        AND event_arrival_time < '2019-12-03'
        AND project_id='aaa'
) t
WHERE rn = 1


【讨论】:

谢谢,但我认为对大量玩家的第一次查询会占用大量资源并且会花费大量时间。第二个似乎不起作用 @IdoBarash 。 . .使用正确的索引,第一个查询可能比聚合更有效。 @IdoBarash:我用索引推荐更新了我的答案。与聚合解决方案相比,我很想知道这对您的真实数据的执行情况。 @GMB 接受的答案使用 nonaggregate 窗口函数 FIRST_VALUE(),如下所述:dev.mysql.com/doc/refman/8.0/en/…。您的代码使用 aggregate 函数 MAX(),如下所述:dev.mysql.com/doc/refman/8.0/en/group-by-functions.html @forpas:我明白了。但是SELECT DISTINCT本质上是聚合。【参考方案3】:

我认为这应该可行。没有样品很难分辨。

SELECT
     t.project_id,
     t.player_id,
     GROUP_CONCAT(t.balance) AS Balance -- supposed to have single value
FROM transacitions t
LEFT JOIN transactions t2 ON t.project_id = t2.project_id AND t.player_id = t2.player_id
AND t.event_arrival_time < t2.event_arrival_time
where t2.player_id IS NULL
AND t.event_arrival_time BETWEEN '2019-12-02 00:00:00' AND '2019-12-03 23:59:59'
GROUP BY t.project_id, t.player_id
ORDER BY t.event_arrival_time desc

【讨论】:

以上是关于MYSql 窗口函数 - 获取最后一个值的主要内容,如果未能解决你的问题,请参考以下文章

窗口分析函数19_Mysql查询窗口函数里第一个最后一个第N个元素的值值案例详解(FIRST_VALUE LAST_VALUE NVH_VALUE)

窗口函数从每个组中获取第一行和最后一行

在 MySQL 8 中使用窗口函数获取不同列的计数

Spark Window 函数:是不是可以直接从使用第一个/最后一个函数找到的行中获取其他值?

MySQL窗口函数_聚合函数

MySQL窗口_分布、前后、头尾函数