——用户变量,系统变量,Join,IF,累加和或前缀和
Table: Activity
+--------------+---------+
| Column Name | Type |
+--------------+---------+
| player_id | int |
| device_id | int |
| event_date | date |
| games_played | int |
+--------------+---------+
(player_id,event_date)是此表的主键。
这张表显示了某些游戏的玩家的活动情况。
每一行是一个玩家的记录,他在某一天使用某个设备注销之前登录并玩了很多游戏(可能是 0 )。
编写一个 SQL 查询,同时报告每组玩家和日期,以及玩家到目前为止玩了多少游戏。也就是说,在此日期之前玩家所玩的游戏总数。详细情况请查看示例。
查询结果格式如下所示:
Activity table:
+-----------+-----------+------------+--------------+
| player_id | device_id | event_date | games_played |
+-----------+-----------+------------+--------------+
| 1 | 2 | 2016-03-01 | 5 |
| 1 | 2 | 2016-05-02 | 6 |
| 1 | 3 | 2017-06-25 | 1 |
| 3 | 1 | 2016-03-02 | 0 |
| 3 | 4 | 2018-07-03 | 5 |
+-----------+-----------+------------+--------------+
Result table:
+-----------+------------+---------------------+
| player_id | event_date | games_played_so_far |
+-----------+------------+---------------------+
| 1 | 2016-03-01 | 5 |
| 1 | 2016-05-02 | 11 |
| 1 | 2017-06-25 | 12 |
| 3 | 2016-03-02 | 0 |
| 3 | 2018-07-03 | 5 |
+-----------+------------+---------------------+
对于 ID 为 1 的玩家,2016-05-02 共玩了 5+6=11 个游戏,2017-06-25 共玩了 5+6+1=12 个游戏。
对于 ID 为 3 的玩家,2018-07-03 共玩了 0+5=5 个游戏。
请注意,对于每个玩家,我们只关心玩家的登录日期。
审题:计算每次登陆后的累计完了多少个游戏,将games_played改为累计数字。
思考:应该是一个求和问题,
解题:
方法一:
SELECT C.player_id,C.event_date,C.games_played_so_far
FROM (
SELECT
A.player_id,
A.event_date,
@sum_cnt:=
if(A.player_id = @pre_id AND A.event_date != @pre_date,
@sum_cnt + A.games_played,
A.games_played
)
AS `games_played_so_far`,
@pre_id:=A.player_id AS `player_ids`,
@pre_date:=A.event_date AS `event_dates`
FROM
activity AS A,(SELECT @pre_id:=NULL,@pre_date:=NULL,@sum_cnt:=0) AS B
order BY A.player_id,A.event_date
) AS C
自己写
select C.id,C.date,C.add From(
selcet A.id,A.date ,
@add := if(A.id = @id and A.date != @date,@add + A.add,A.add),
@id = A.id,
@date = A.date, FROM activity AS A,
(select @id := null, @date:= null,@add := 0)AS B
) AS C;
解题过程:
首先先按照id和日期排序。
定义用户变量:pre_id:上一行的player_id,pre_date: 上一行的event_date,sum_cnt:每个人games_played前缀和。
(select @pre_id:=Null,@pre_date:=Null,@sum_cnt:=0) As B
累计的计算逻辑:id相同,日期不同,就需要加上。如果并不符合就是本身次数就是次数的和:
if (当前行的player_id = pre_id and 当前行的event_date != pre_date){
sum_cnt = sum_cnt + 当前行的games_played
}else{
sum_cnt = games_played
}
转换为sql:
SELECT
A.player_id,
A.event_date,
##如果条件成立执行+,条件不成立结果就是和。
@sum_cnt:= IF(A.player_id = @pre_id AND A.event_date != @pre_date,
@sum_cnt + A.games_played,
A.games_played
) AS `games_played_so_far`,
##给变量赋值
@pre_id:=A.player_id AS `player_ids`,
@pre_date:=A.event_date AS `event_dates`
FROM
activity AS A,(
SELECT @pre_id:= NULL,@pre_date:= NULL,@sum_cnt:=0) AS B
ORDER BY A.player_id,A.event_date
但由于用户变量的限制,多出了两个字段player_ids,event_dates。
再投一次影,去掉多出的字段,即可得出第一种解法。
方法2:将当前日期和之前的玩过游戏的数量加起来。
同一个,在日期A当天和之前,用表自连接,group by,找出这些行。也是这类累加和或前缀和问题的通用解法:
SELECT *
FROM Activity AS A JOIN Activity AS B ON (A.player_id = B.player_id AND A.event_date <= B.event_date)
id相等,日期不同。
在加上排序和组内求和就是结果:
SELECT B.player_id,B.event_date,SUM(A.games_played) AS `games_played_so_far`
FROM Activity AS A JOIN Activity AS B
ON (A.player_id = B.player_id AND A.event_date <= B.event_date)
GROUP BY B.player_id,B.event_date
自己写
select B.id ,B.date, SUM(A.add) from Activity AS A JOIN AS B ON (A.id = B.id AND A.date <= B.date) GROUP BY B.id, B.date;
知识点:
MySql用户变量:基于会话变量实现的, 可以暂存值, 并传递给同一连接里的下一条sql使用的变量.当客户端连接退出时,变量会被释放,用户变量:以"@“开始,形式为”@变量名",用户变量跟mysql客户端是绑定的,设置的变量,只对当前用户使用的客户端生效。也叫会话变量。
MySql用户变量赋值:如果使用没有初始化的变量,其值是NULL。使用set赋值时,使用的是“=”或者":=",使用select赋值时必须使用的是“:="。
MySql的IF():类似于Java的三目运算。
Mysql系统变量:以"@@“开始,形式为”@@变量名"。本题未涉及。
来源:CSDN
作者:DuanXS97
链接:https://blog.csdn.net/qq_43618030/article/details/104215220