【问题标题】:SQL Moving Window on Group BySQL 在 Group By 上移动窗口
【发布时间】:2021-10-12 16:29:04
【问题描述】:

我有一个有几个玩家的桌子,每个玩家都可以选择玩游戏AB(只有 2 个游戏可用)并记录他们的日期和时间:

Player Date Time Game
1 2021-01-01 1:00 A
1 2021-01-02 1:00 A
1 2021-01-03 1:00 A
1 2021-01-04 1:00 A
1 2021-01-05 1:00 A
1 2021-01-06 1:00 A
1 2021-01-07 1:00 A
1 2021-01-08 1:00 A
2 2021-01-01 5:00 A
2 2021-01-02 6:00 B
2 2021-01-03 1:00 B
2 2021-01-04 3:00 A
2 2021-01-05 2:00 A
2 2021-01-06 4:00 A
2 2021-01-07 9:00 A
2 2021-01-08 1:00 B
3 2021-01-01 5:00 A
3 2021-01-02 6:00 A
3 2021-01-03 1:00 B
3 2021-01-04 3:00 A
3 2021-01-05 2:00 B
3 2021-01-06 4:00 A
3 2021-01-07 5:00 B
3 2021-01-07 6:00 A
3 2021-01-07 7:00 B
3 2021-01-07 9:00 A
3 2021-01-08 1:00 B
4 2021-01-02 2:00 A
4 2021-01-03 2:00 NULL
4 2021-01-04 4:00 NULL
4 2021-01-05 1:00 NULL
4 2021-01-06 9:00 NULL
4 2021-01-07 8:00 B

对于每个玩家,我都在寻找一个高效且可移植的 SQL 查询,以确定他们使用三(或四)天窗口玩过的独特/独特游戏的数量。请注意,玩游戏的Time(一天内)无关紧要。因此,三天窗口的结果如下所示:

Player Start Date End Date Unique Games Played
1 2021-01-01 2021-01-03 1
1 2021-01-02 2021-01-04 1
1 2021-01-03 2021-01-05 1
1 2021-01-04 2021-01-06 1
1 2021-01-05 2021-01-07 1
1 2021-01-06 2021-01-08 1
2 2021-01-01 2021-01-03 2
2 2021-01-02 2021-01-04 2
2 2021-01-03 2021-01-05 2
2 2021-01-04 2021-01-06 1
2 2021-01-05 2021-01-07 1
2 2021-01-06 2021-01-08 2
3 2021-01-01 2021-01-03 2
3 2021-01-02 2021-01-04 2
3 2021-01-03 2021-01-05 2
3 2021-01-04 2021-01-06 2
3 2021-01-05 2021-01-07 2
3 2021-01-06 2021-01-08 2
4 2021-01-02 2021-01-04 1
4 2021-01-03 2021-01-05 0
4 2021-01-04 2021-01-06 0
4 2021-01-05 2021-01-07 1

我从类似的东西开始:

SELECT COUNT(DISTINCT GAME)
FROM PLAY_TABLE
GROUP BY PLAYER
ORDER BY DATE

但这还不够,因为它计算的是在整个日期范围内玩的所有不同游戏,而且我不确定如何在每个组内的三天内滚动一个窗口。我了解某些 SQL 数据库风格提供窗口/分析功能,但我更喜欢通用解决方案。

【问题讨论】:

  • 用您正在使用的数据库标记您的问题。

标签: sql group-by sql-order-by ansi-sql


【解决方案1】:

如果您有用户和日期,则可以使用生成您关心的用户和日期组合的查询。然后可以left join回表获取匹配用户和日期时间段的原始行并聚合:

select p.player, d.date, d.date + interval '2 day',
       count(distinct t.game) as num_games
from (select distinct player from t) p cross join
     (<whatever dates you want>) d left join
     t
     on t.player = p.player and
        t.date = d.date and
        t.date <= d.date + interval '2 day'
group by p.player, d.date;

日期和时间算法是特定于数据库的,因此确切的语法取决于您的数据库。

【讨论】:

  • ANSI SQL 想要interval '2' day
  • 也许我理解错了,但p 应该只包含来自select distinct player from t 的结果,这意味着p.date 不存在。然而p.date 被用于最后的on 条件。也许on 条件应该是ON ( t.player = p.player and t.date &gt;= d.date and t.date &lt;= d.date + interval '2' day)
【解决方案2】:

将结合ARRAY_AGGFLATTENARRAY_DISTINCTFILTERCARDINALITY

select    player, Date as start_date, date_add(Date, 2) as end_date
        , cardinality(filter(array_distinct(flatten(array_agg(array_agg(Game)) 
          over (partition by player order by Date rows between current row and 2 following ))),
          x -> x is not null )) as unique_games_played
from PLAY_TABLE
group by player, Date
order by player, start_date

https://prestodb.io/docs/current/functions/array.html 中描述的数组函数

【讨论】:

  • 您确定 OP 使用的是 Presto 吗? (此产品特定答案,不符合 ANSI SQL。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多