第一篇
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 the first login date for each player.
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 |
| 2 | 3 | 2017-06-25 | 1 |
| 3 | 1 | 2016-03-02 | 0 |
| 3 | 4 | 2018-07-03 | 5 |
+-----------+-----------+------------+--------------+
Result table:
+-----------+-------------+
| player_id | first_login |
+-----------+-------------+
| 1 | 2016-03-01 |
| 2 | 2017-06-25 |
| 3 | 2016-03-02 |
+-----------+-------------+
找出每个玩家第一次登录的日期。
按玩家分组,求每个玩家登录日期的最小值。
select player_id,min(event_date) as `first_login`
from Activity
group by player_id
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 a SQL query that reports the device that is first logged in for each player.
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 |
| 2 | 3 | 2017-06-25 | 1 |
| 3 | 1 | 2016-03-02 | 0 |
| 3 | 4 | 2018-07-03 | 5 |
+-----------+-----------+------------+--------------+
Result table:
+-----------+-----------+
| player_id | device_id |
+-----------+-----------+
| 1 | 2 |
| 2 | 3 |
| 3 | 1 |
+-----------+-----------+
select A.player_id,A.device_id
from Activity as A
where A.event_date = (
select event_date
from Activity
where player_id = A.player_id
order by event_date
limit 0,1
)
select A.player_id,A.device_id
from
Activity as A
join
(
select player_id,min(event_date) as `mdate`
from Activity
group by player_id
) as B
on(A.player_id = B.player_id and A.event_date = B.mdate)
select A.player_id,A.device_id
from Activity as A
where (A.player_id,A.event_date) in
(
select player_id,min(event_date) as `mdate`
from Activity
group by player_id
)
解法一
对每个玩家,判断其是不是玩家登录的最早时间。登录的最早时间通过子查询判断。
select A.player_id,A.device_id
from Activity as A
where A.event_date = (
select event_date
from Activity
where player_id = A.player_id
order by event_date
limit 0,1
)
解法二
对玩家分组,找出每个玩家的最早登录日期。
(
select player_id,min(event_date) as `mdate`
from Activity
group by player_id
) as B
再连接Activity表,筛选出每个玩家,最早登录日期时的设备。
select A.player_id,A.device_id
from
Activity as A
join
(
select player_id,min(event_date) as `mdate`
from Activity
group by player_id
) as B
on(A.player_id = B.player_id and A.event_date = B.mdate)
不用连接,用in也行的。
select A.player_id,A.device_id
from
Activity as A
where (A.player_id,A.event_date) in
(
select player_id,min(event_date) as `mdate`
from Activity
group by player_id
)
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.
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
分析每个玩家在日期A之前玩游戏的个数。
解法一
先按player_id和event_date升序,再统计每个人的玩游戏的前缀和。
对于前缀和,用户变量解决更合适。
用户变量:pre_id——上一行的player_id,pre_date—— 上一行的event_date,sum_cnt——每个人games_played前缀和。
(SELECT @pre_id:=NULL,@pre_date:=NULL,@sum_cnt:=0) AS B
前缀和计算逻辑:
if (当前行的player_id = pre_id and 当前行的event_date != pre_date){
sum_cnt = sum_cnt + 当前行的games_played
}else{
sum_cnt = games_played
}
连接表Activity和表B:
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。
再投一次影,去掉多出的字段。
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
解法二
将每个人,在日期A当天和之前,玩过的游戏个数累加。
同一个,在日期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)
补上group by及组内求和。
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