题目
Table: Activity
+--------------+---------+
| Column Name | Type |
+--------------+---------+
| player_id | int |
| device_id | int |
| event_date | date |
| games_played | int |
+--------------+---------+
(player_id, event_date) is the primary key of this table.
This table shows the activity of players of some game.
Each row is a record of a player who logged in and played a number of games (possibly 0) before logging out on some day using some device.
Write an SQL query that reports for each player and date, how many games played so far by the player. That is, the total number of games played by the player until that date. Check the example for clarity.
The query result format is in the following example:
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 |
+-----------+------------+---------------------+
For the player with id 1, 5 + 6 = 11 games played by 2016-05-02, and 5 + 6 + 1 = 12 games played by 2017-06-25.
For the player with id 3, 0 + 5 = 5 games played by 2018-07-03.
Note that for each player we only care about the days when the player logged in.
审题
分析每个玩家在日期A之前玩游戏的个数。
不太会做,直接看答案了。。
方法
1.我感觉我能想到的方法就是引入变量了
设定初值 前一个的id 前一个的日期 累计次数
(SELECT @pre_id:=NULL, @pre_date:=NULL, @sum_count:=0);
利用变量来进行累计
在时间升序的情况下 如果当前id与前一id相等且当前日期大于前一日期 则进行累加@sum_count + A.games_played 否则赋值A.games_played
@sum_count := IF(
A.`player_id` = @pre_id
AND A.`event_date` > @pre_date,
@sum_count + A.games_played,
A.games_played
),
把当前id与日期赋值给前一id与日期变量
@pre_id := A.`player_id`,
@pre_date := A.`event_date`
整体
SELECT
A.`player_id`,
A.`event_date`,
@sum_count := IF(
A.`player_id` = @pre_id
AND A.`event_date` > @pre_date,
@sum_count + A.games_played,
A.games_played
) AS COUNT,
@pre_id := A.`player_id`,
@pre_date := A.`event_date`
FROM
activity AS A,
(SELECT
@pre_id := NULL,
@pre_date := NULL,
@sum_count := 0) AS tmp
ORDER BY A.`player_id`, A.`event_date`;
结果
为去掉多于列 将这个结果作为临时表再做一次查询
SELECT tmp.player_id AS player_id,
tmp.event_date AS event_date,
tmp.count AS games_played_so_far
FROM(
SELECT
A.`player_id`,
A.`event_date`,
@sum_count := IF(
A.`player_id` = @pre_id
AND A.`event_date` > @pre_date,
@sum_count + A.games_played,
A.games_played
) AS COUNT,
@pre_id := A.`player_id`,
@pre_date := A.`event_date`
FROM
activity AS A,
(SELECT
@pre_id := NULL,
@pre_date := NULL,
@sum_count := 0) AS tmp
ORDER BY A.`player_id`, A.`event_date`) tmp;
和题目要求的结果有一些出入是因为数据有一些差别
2.嵌套查询出每个日期前玩的总数即可。
很好理解的 对于每一个日期选出id相同且日期在其之前的游玩次数进行求和
SELECT
A.`player_id`,
A.`event_date`,
(SELECT
SUM(A2.`games_played`)
FROM
activity AS A2
WHERE A.`player_id` = A2.`player_id`
AND A.`event_date` >= A2.`event_date`) AS 'games_played_so_far'
FROM
activity AS A;
3.很多答案都是这样做的 可能就是这种题目的一个框架
累加型题目,可以考虑使用笛卡尔积进行自表连接,连接后的表进行where条件进行筛选、group by分组操作。
先尝试一下自连接
SELECT *
FROM activity A
JOIN activity B
ON A.`player_id` = B.`player_id` AND A.`event_date` <=B.`event_date`
重点关注这三列
SELECT A.`games_played`, B.`event_date`
FROM activity A
JOIN activity B
ON A.`player_id` = B.`player_id` AND A.`event_date` <=B.`event_date`
这个结果就可以帮助我们实现需求
SELECT
A.`player_id`,
B.`event_date`,
SUM(A.`games_played`) AS games_played_so_far
FROM
activity A
JOIN activity B
ON A.`player_id` = B.`player_id`
AND A.`event_date` <= B.`event_date`
GROUP BY A.`player_id`,
B.`event_date`
ORDER BY A.`player_id`,
B.`event_date` ;