玩家平均连胜时长的 SQL 查询

Posted

技术标签:

【中文标题】玩家平均连胜时长的 SQL 查询【英文标题】:SQL Query for players average hot streak duration 【发布时间】:2018-09-20 02:16:48 【问题描述】:

假设我有一个包含三列和以下数据的表:

* gamedate,playername,pointsScored
* 20180101,George,34
* 20180102,George,37
* 20180103,George,38
* 20180104,George,3
* 20180105,George,35
* 20180106,George,37
* 20180107,George,31
* 20180108,George,31
* 20180109,George,32
* 20180110,George,7

我正在寻找一个sql query,它可以告诉我该玩家平均连续多少场比赛可以保持 30+ 的分数。我知道它必须是某种group by 函数,但我有点坚持如何制定这个查询。这是Oracle table,如果这有什么不同的话。

一些额外的测试日期:

with
inputs(gamedate,playername,pointsscored) as (
select to_date('20180912','yyyymmdd'), 'George',52 from dual union all
select to_date('20180907','yyyymmdd'), 'George',47 from dual union all
select to_date('20180829','yyyymmdd'), 'George',9 from dual union all
select to_date('20180823','yyyymmdd'), 'George',55 from dual union all
select to_date('20180818','yyyymmdd'), 'George',49 from dual union all
select to_date('20180811','yyyymmdd'), 'George',58 from dual union all
select to_date('20180805','yyyymmdd'), 'George',31 from dual union all
select to_date('20180730','yyyymmdd'), 'George',40 from dual union all
select to_date('20180720','yyyymmdd'), 'George',44 from dual union all
select to_date('20180712','yyyymmdd'), 'George',45 from dual union all
select to_date('20180707','yyyymmdd'), 'George',29 from dual union all
select to_date('20180701','yyyymmdd'), 'George',-5 from dual union all
select to_date('20180626','yyyymmdd'), 'George',46 from dual union all
select to_date('20180620','yyyymmdd'), 'George',22 from dual union all
select to_date('20180614','yyyymmdd'), 'George',49 from dual union all
select to_date('20180609','yyyymmdd'), 'George',40 from dual union all
select to_date('20180602','yyyymmdd'), 'George',40 from dual
)

【问题讨论】:

试图了解问题...在您的示例中,我们是否正在查看两连胜,三场比赛中的一场和五场比赛中的另一场,并且您要寻找的平均数是 4?不管有多少场得分低于30分的比赛是在牛排之间,还是在第一场之前或最后一场之后? 是的,mathguy 是正确的 【参考方案1】:

在 Oracle 12.1 或更高版本中,MATCH_RECOGNIZE 子句可以快速完成此类分配:

with
  inputs(gamedate,playername,pointsscored) as (
    select to_date('20180101','yyyymmdd'), 'George', 34 from dual union all
    select to_date('20180102','yyyymmdd'), 'George', 37 from dual union all
    select to_date('20180103','yyyymmdd'), 'George', 38 from dual union all
    select to_date('20180104','yyyymmdd'), 'George',  3 from dual union all
    select to_date('20180105','yyyymmdd'), 'George', 35 from dual union all
    select to_date('20180106','yyyymmdd'), 'George', 37 from dual union all
    select to_date('20180107','yyyymmdd'), 'George', 31 from dual union all
    select to_date('20180108','yyyymmdd'), 'George', 31 from dual union all
    select to_date('20180109','yyyymmdd'), 'George', 32 from dual union all
    select to_date('20180109','yyyymmdd'), 'George',  7 from dual
  )
select playername, avg(cnt) as average_streak
from   inputs
match_recognize(
  partition by playername
  order by     gamedate
  measures     count(*) as cnt
  one row per match
  pattern ( a+ )
  define a as pointsscored >= 30
)
group by playername
;

PLAYER AVERAGE_STREAK
------ --------------
George              4

解释:

MATCH_RECOGNIZE 按玩家对输入行进行分区,并按游戏日期在每个分区内对它们进行排序。 “匹配”是出现一个或多个连续行(PATTERN 子条款中的a+),得分为 30 或更多(参见DEFINE 子条款)。 MATCH_RECOGNIZE 为它找到的每场比赛返回一行,特别是它返回球员姓名和比赛中的行数(这是连胜的长度,连续行数或游戏数)。外部查询按玩家分组并采用平均连胜长度。

【讨论】:

你知道你把答案翻了一倍吗朋友:) mathguy 非常感谢!这正是我所需要的。而且查询就是这么简单! @BarbarosÖzhan - 我假设你并不是真的意味着我“加倍”了答案(意思是,两次发布相同的答案)。相反,我发布了同一个问题的两个答案。是的,我知道 - 我有时会这样做,当我为同一个问题提供两个完全不同且不相关的答案时(通常,一个答案可能更好,但仅适用于最新版本的 Oracle)。据我所知,这没关系,应该如此。 是的,我的意思是第二种情况“同一个问题的两个答案”。【参考方案2】:

这是一种方法。它使用 Tabibitosan(或“固定差异”)方法来识别条纹 - 这是内部查询中两个 ROW_NUMBER 调用的差异。剩下的就是对聚合函数进行分组和获取。

WITH子句是模拟数据;它不是解决方案的一部分,应在根据实际数据测试解决方案之前将其删除。 (在内部查询中使用您的实际表名而不是 INPUTS。)

with
  inputs(gamedate,playername,pointsscored) as (
    select to_date('20180101','yyyymmdd'), 'George', 34 from dual union all
    select to_date('20180102','yyyymmdd'), 'George', 37 from dual union all
    select to_date('20180103','yyyymmdd'), 'George', 38 from dual union all
    select to_date('20180104','yyyymmdd'), 'George',  3 from dual union all
    select to_date('20180105','yyyymmdd'), 'George', 35 from dual union all
    select to_date('20180106','yyyymmdd'), 'George', 37 from dual union all
    select to_date('20180107','yyyymmdd'), 'George', 31 from dual union all
    select to_date('20180108','yyyymmdd'), 'George', 31 from dual union all
    select to_date('20180109','yyyymmdd'), 'George', 32 from dual union all
    select to_date('20180109','yyyymmdd'), 'George',  7 from dual
  )
select   playername, avg(ct) as average_streak
from     (
           select playername, count(*) as ct
           from   (
                    select playername, pointsscored,
                           row_number() over (partition by playername 
                                                order by gamedate)
                           - row_number() over (partition by playername,
                                                 case when pointsscored >= 30 
                                                      then 0 end
                                                 order by gamedate) as grp
                    from   inputs
                  )
           group by playername, grp
           having   min(pointsscored) >= 30
         )
group by playername
order by playername
;

输出

PLAYER AVERAGE_STREAK
------ --------------
George              4

【讨论】:

mathguy 我能够运行此查询,但它没有给出我对某些数据集的预期答案。我用我用来测试的数据更新了我的问题。 @themosquito - 我刚刚用新数据运行了这个查询。我得到的结果是 3.25,这是正确的:有四个连胜,长度分别为 2、7、1 和 3。13/4 = 3.25,或者您是否考虑长度为 1 的“连胜”(单场比赛超过 30点,在两场比赛中得分低于 30 分)为“不是连续”?在这两种解决方案中,我认为这是长度为 1 的连胜。如果单场 30 分以上的比赛不是连胜,那么必须修改两个查询,并且对于这个不同的定义,两者都可以很容易地修改。 (另外,乔治真的在一场比赛中得了负五分吗?) 这太奇怪了,它得到的结果是 2 使用我在问题末尾粘贴的完全相同的数据运行查询。但是是的,你是对的,答案应该是 3.25。是的,乔治在一场比赛中确实得分-5。因此,我很想知道,一个大部分得分超过 40 分的真正优秀球员如何在每 3 场或第 4 场比赛中表现不佳。糟糕的比赛不会与更难对付的对手相提并论。所以我想对数据进行一些分析,看看他和其他在他位置上的球员平均能打多少场比赛才能保持连胜,然后才打出一场糟糕的比赛。 :) @themosquito - 一个球员,任何球员,怎么可能得分负数?你的意思是他得了5分?得分-5分意味着什么?在足球比赛中,您可能会将自己的进球视为负进球;这对篮球意味着什么?

以上是关于玩家平均连胜时长的 SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章

应用内广告帮助乐易实现收入+玩家时长双丰收

如何计算每位球员的最长连胜纪录

sql查询平均下单时间

上周每位玩家的平均游戏数

2022-12-13:游戏玩法分析 I。写一条 SQL 查询语句获取每位玩家 第一次登陆平台的日期。 +-----------+-------------+ | player_id | first_l

黄金点