oracle 解决ORA-00937:不是单组群函数

vql8enpb  于 2023-04-20  发布在  Oracle
关注(0)|答案(3)|浏览(111)

我在解决一个问题
表:Activity

+--------------+---------+
| Column Name  | Type    |
+--------------+---------+
| player_id    | int     |
| device_id    | int     |
| event_date   | date    |
| games_played | int     |
+--------------+---------+

(player_id, event_date)是这个表的主键。这个表显示了一些游戏的玩家的活动。
每一行都是一个玩家的记录,他在某一天使用某个设备登录并玩了很多游戏(可能是0)。
编写一个SQL查询,报告在第一次登录后第二天再次登录的玩家比例,四舍五入到小数点后2位。换句话说,您需要计算从第一次登录日期开始至少连续两天登录的玩家数量,然后将该数字除以玩家总数。查询结果格式如下例所示。
输入:
Activity表:

+-----------+-----------+------------+--------------+
| player_id | device_id | event_date | games_played |
+-----------+-----------+------------+--------------+
| 1         | 2         | 2016-03-01 | 5            |
| 1         | 2         | 2016-03-02 | 6            |
| 2         | 3         | 2017-06-25 | 1            |
| 3         | 1         | 2016-03-02 | 0            |
| 3         | 4         | 2018-07-03 | 5            |
+-----------+-----------+------------+--------------+

输出:

+-----------+
| fraction  |
+-----------+
| 0.33      |
+-----------+

说明:
只有id为1的玩家在他登录的第一天后重新登录,所以答案是1/3 = 0.33
我写了这个问题:

select 
    round(count(a1.player_id) / (select count(distinct player_id) as cnt from activity a3), 2)
from
    (select 
         activity.*, 
         row_number() over (partition by player_id order by event_date) as rn
     from 
         activity) a1 
join 
    activity a2 on a1.rn = 1 
                and a2.event_date - a1.event_date = 1 
                and a1.player_id = a2.player_id;

但我得到以下错误:
第32行错误:
ORA-00937:不是单组组函数
编辑:
我实际上是用这个解决的:

with retained as (
  select count(*) as ret
  from
  (
      select activity.*, row_number() over(partition by player_id order by event_date) as rn
      from activity
  ) a1 join activity a2
  on
  a1.rn = 1 and
  a2.event_date - a1.event_date = 1 and
  a1.player_id = a2.player_id
),
total as (
  select count(distinct player_id) as cnt
  from activity
)
select distinct round(ret/cnt, 2) as fraction from retained, total;
;

与我最初的解决方案的唯一区别是我使用单独的语句进行了重构。但我仍然不确定为什么我的原始解决方案是错误的。

tzcvj98z

tzcvj98z1#

在Oracle 12中,您可以使用MATCH_RECOGNIZE执行逐行模式匹配,并查找玩家的第二次登录时间是第一次登录后的第二天,然后使用条件聚合来查找分数:

SELECT ROUND(COUNT(day2)/COUNT(*), 2) AS fraction
FROM   activity
MATCH_RECOGNIZE(
  PARTITION BY player_id
  ORDER     BY event_date
  MEASURES
    day2.event_date AS day2
  PATTERN ( ^ day1 day2? )
  DEFINE
    day2 AS day2.event_date = day1.event_date + 1
)

或者,如果你想使用解析函数,那么:

SELECT ROUND(
         COUNT(CASE WHEN event_date = min_date + 1 THEN player_id END)
         / COUNT(CASE WHEN event_date = min_date THEN player_id END),
         2
       ) AS fraction
FROM   (
  SELECT player_id,
         event_date,
         MIN(event_date) OVER (PARTITION BY player_id) AS min_date
  FROM   activity
)

其中,对于样本数据:

CREATE TABLE activity (player_id, device_id, event_date, games_played) AS
  SELECT 1, 2, DATE '2016-03-01', 5 FROM DUAL UNION ALL
  SELECT 1, 2, DATE '2016-03-02', 6 FROM DUAL UNION ALL
  SELECT 2, 3, DATE '2017-06-25', 1 FROM DUAL UNION ALL
  SELECT 3, 1, DATE '2016-03-02', 0 FROM DUAL UNION ALL
  SELECT 3, 4, DATE '2018-07-03', 5 FROM DUAL;

两个输出:
| 分数|
| --------------|
| 点三三口径|
fiddle

xdyibdwo

xdyibdwo2#

尝试使用窗口函数count()

select distinct round(count(a2.player_id) over() / count_players, 2) as fraction
from
    (select 
         activity.*, 
         row_number() over (partition by player_id order by event_date) as rn,
         count(distinct PLAYER_ID) over () as count_players
     from 
         activity
    ) a1 
left join 
    activity a2 on a1.rn = 1 
                and a2.event_date - a1.event_date = 1 
                and a1.player_id = a2.player_id;

使用left join而不是join将有助于在任何参与者的条件不匹配时返回预期结果。
Demo here

cfh9epnr

cfh9epnr3#

您可以使用分析函数(Count()Over()和Lead()Over())来获取这两个值-玩家总数和日期差异,以查看日志是否在连续的几天(DAY_DIFF = 1)。更新:

WITH         --  Sample data
    activity AS
        (   Select 1 "PLAYER_ID", 2 "DEVICE_ID", To_Date('2016-03-01', 'yyyy-mm-dd') "EVENT_DATE", 5 "GAMES_PLAYED" From Dual Union All
            Select 1 "PLAYER_ID", 2 "DEVICE_ID", To_Date('2016-03-02', 'yyyy-mm-dd') "EVENT_DATE", 6 "GAMES_PLAYED" From Dual Union All
            Select 2 "PLAYER_ID", 3 "DEVICE_ID", To_Date('2017-06-25', 'yyyy-mm-dd') "EVENT_DATE", 1 "GAMES_PLAYED" From Dual Union All
            Select 3 "PLAYER_ID", 1 "DEVICE_ID", To_Date('2016-03-02', 'yyyy-mm-dd') "EVENT_DATE", 0 "GAMES_PLAYED" From Dual Union All
            Select 3 "PLAYER_ID", 4 "DEVICE_ID", To_Date('2018-07-03', 'yyyy-mm-dd') "EVENT_DATE", 5 "GAMES_PLAYED" From Dual 
        )
--  Main SQL
Select 
    Round(Count(DISTINCT PLAYER_ID) / Max(TOTAL_PLAYERS), 2) "fraction"
From
    (
        Select 
          PLAYER_ID, 
          EVENT_DATE,
          Sum(1) OVER(Partition By PLAYER_ID Order By PLAYER_ID, EVENT_DATE) "LOGGING_NO",
          Count(DISTINCT PLAYER_ID) OVER() "TOTAL_PLAYERS",
          Nvl(LEAD(EVENT_DATE) OVER(Partition By PLAYER_ID Order By PLAYER_ID, EVENT_DATE) - EVENT_DATE, 0) "DAY_DIFF"
        From
          activity
    )
Where DAY_DIFF = 1 And LOGGING_NO = 1
--
--
  fraction
----------
       .33

在内部查询中添加了一个分析列,以获取第一个(和其他)日志记录。主要工作已经通过内部查询完成,结果如下:

PLAYER_ID EVENT_DAT LOGGING_NO TOTAL_PLAYERS   DAY_DIFF
---------- --------- ---------- ------------- ----------
         1 01-MAR-16          1             3          1
         1 02-MAR-16          2             3          0
         2 25-JUN-17          1             3          0
         3 02-MAR-16          1             3        853
         3 03-JUL-18          2             3          0

外部选择只是计算每天连续登录的不同玩家,然后将其除以玩家总数。好吧,它在那里工作-他们只是出于某种原因不满意列名“结果”。将其更改为“分数”,如下所示:

相关问题