SQL SELECT 子句中的子查询
SQL subquery in SELECT clause
我试图在过去 30 天内找到管理员 activity。
账户table存储用户数据(用户名、密码等)
每天结束时,如果用户登录,它将在 player_history table 中创建一个新条目,其中包含更新后的数据。这样我们就可以随着时间的推移跟踪进度。
帐户table:
id
用户名
管理员
1
迈克尔
4
2
史蒂夫
3
3
路易丝
3
4
乔
0
5
艾米
1
player_history table:
id
user_id
created_at
游戏时间
0
1
2021-04-03
10
1
2
2021-04-04
10
2
3
2021-04-05
15
3
4
2021-04-10
20
4
5
2021-04-11
20
5
1
2021-05-12
40
6
2
2021-05-13
55
7
3
2021-05-17
65
8
4
2021-05-19
75
9
5
2021-05-23
30
10
1
2021-06-01
60
11
2
2021-06-02
65
12
3
2021-06-02
67
13
4
2021-06-03
90
以下查询
SELECT a.`username`, SEC_TO_TIME((MAX(h.`playtime`) - MIN(h.`playtime`))*60) as 'time' FROM `player_history` h, `accounts` a WHERE h.`created_at` > '2021-05-06' AND h.`user_id` = a.`id` AND a.`admin` > 0 GROUP BY h.`user_id`
输出这个 table:
请注意,这只是管理员 activity,因此此数据中不包括 Joe。
从 2021-05-06 至今 (yy-mm-dd):
用户名
时间
迈克尔
00:20:00
史蒂夫
00:10:00
路易丝
00:02:00
艾米
00:00:00
从数据可以看出,Amy在上个月已经玩了10分钟,但她的时间显示为0。这是因为她只有 1 个从 2021-05-06 开始的条目,所以没有数据可以比较。它是 0 因为 10-10 = 0.
还有一个缺陷就是没有把上个月的所有activity都包括进去,基本上只是用最低值减去最高值。
因此,我尝试通过将 2021-05-06 之后的最高值与该日期之前的上一次登录进行比较来解决此问题。所以我稍微修改了查询:
SELECT a.`Username`, SEC_TO_TIME((MAX(h.`playtime`) - (SELECT MAX(`playtime`) FROM `player_history` WHERE a.`id` = `user_id` AND `created_at` < '2021-05-06'))*60) as 'Time' FROM `player_history` h, `accounts` a WHERE h.`created_at` >= '2021-05-06' AND h.`user_id` = a.`id` AND a.`admin` > 0 GROUP BY h.`user_id`
所以现在它会输出:
用户名
时间
迈克尔
00:50:00
史蒂夫
00:50:00
路易丝
00:52:00
艾米
00:10:00
但我觉得整个查询效率很低。有更好的方法吗?
我想你想要lag()
:
SELECT a.username,
SEC_TO_TIME(SUM(h.playtime - COALESCE(h.prev_playtime, 0))) as time
FROM accounts a JOIN
(SELECT h.*,
LAG(playtime) OVER (PARTITION BY u.user_id ORDER BY h.created_at) as prev_playtime
FROM player_history h
) h
ON h.user_id = a.id
WHERE h.created_at > '2021-05-06' AND
a.admin > 0
GROUP BY a.username;
除了 LAG()
逻辑之外,请注意查询的其他更改:
- 使用正确、明确、标准、可读的
JOIN
语法。
SELECT
和 GROUP BY
使用一致的列。
- 删除了列别名周围的单引号。
- 去除反引号;它们只会使查询变得混乱,使其更难编写和阅读。
我试图在过去 30 天内找到管理员 activity。
账户table存储用户数据(用户名、密码等)
每天结束时,如果用户登录,它将在 player_history table 中创建一个新条目,其中包含更新后的数据。这样我们就可以随着时间的推移跟踪进度。
帐户table:
id | 用户名 | 管理员 |
---|---|---|
1 | 迈克尔 | 4 |
2 | 史蒂夫 | 3 |
3 | 路易丝 | 3 |
4 | 乔 | 0 |
5 | 艾米 | 1 |
player_history table:
id | user_id | created_at | 游戏时间 |
---|---|---|---|
0 | 1 | 2021-04-03 | 10 |
1 | 2 | 2021-04-04 | 10 |
2 | 3 | 2021-04-05 | 15 |
3 | 4 | 2021-04-10 | 20 |
4 | 5 | 2021-04-11 | 20 |
5 | 1 | 2021-05-12 | 40 |
6 | 2 | 2021-05-13 | 55 |
7 | 3 | 2021-05-17 | 65 |
8 | 4 | 2021-05-19 | 75 |
9 | 5 | 2021-05-23 | 30 |
10 | 1 | 2021-06-01 | 60 |
11 | 2 | 2021-06-02 | 65 |
12 | 3 | 2021-06-02 | 67 |
13 | 4 | 2021-06-03 | 90 |
以下查询
SELECT a.`username`, SEC_TO_TIME((MAX(h.`playtime`) - MIN(h.`playtime`))*60) as 'time' FROM `player_history` h, `accounts` a WHERE h.`created_at` > '2021-05-06' AND h.`user_id` = a.`id` AND a.`admin` > 0 GROUP BY h.`user_id`
输出这个 table:
请注意,这只是管理员 activity,因此此数据中不包括 Joe。
从 2021-05-06 至今 (yy-mm-dd):
用户名 | 时间 |
---|---|
迈克尔 | 00:20:00 |
史蒂夫 | 00:10:00 |
路易丝 | 00:02:00 |
艾米 | 00:00:00 |
从数据可以看出,Amy在上个月已经玩了10分钟,但她的时间显示为0。这是因为她只有 1 个从 2021-05-06 开始的条目,所以没有数据可以比较。它是 0 因为 10-10 = 0.
还有一个缺陷就是没有把上个月的所有activity都包括进去,基本上只是用最低值减去最高值。
因此,我尝试通过将 2021-05-06 之后的最高值与该日期之前的上一次登录进行比较来解决此问题。所以我稍微修改了查询:
SELECT a.`Username`, SEC_TO_TIME((MAX(h.`playtime`) - (SELECT MAX(`playtime`) FROM `player_history` WHERE a.`id` = `user_id` AND `created_at` < '2021-05-06'))*60) as 'Time' FROM `player_history` h, `accounts` a WHERE h.`created_at` >= '2021-05-06' AND h.`user_id` = a.`id` AND a.`admin` > 0 GROUP BY h.`user_id`
所以现在它会输出:
用户名 | 时间 |
---|---|
迈克尔 | 00:50:00 |
史蒂夫 | 00:50:00 |
路易丝 | 00:52:00 |
艾米 | 00:10:00 |
但我觉得整个查询效率很低。有更好的方法吗?
我想你想要lag()
:
SELECT a.username,
SEC_TO_TIME(SUM(h.playtime - COALESCE(h.prev_playtime, 0))) as time
FROM accounts a JOIN
(SELECT h.*,
LAG(playtime) OVER (PARTITION BY u.user_id ORDER BY h.created_at) as prev_playtime
FROM player_history h
) h
ON h.user_id = a.id
WHERE h.created_at > '2021-05-06' AND
a.admin > 0
GROUP BY a.username;
除了 LAG()
逻辑之外,请注意查询的其他更改:
- 使用正确、明确、标准、可读的
JOIN
语法。 SELECT
和GROUP BY
使用一致的列。- 删除了列别名周围的单引号。
- 去除反引号;它们只会使查询变得混乱,使其更难编写和阅读。