题目
支出表: Spending
+-------------+---------+
| Column Name | Type |
+-------------+---------+
| user_id | int |
| spend_date | date |
| platform | enum |
| amount | int |
+-------------+---------+
这张表记录了用户在一个在线购物网站的支出历史,该在线购物平台同时拥有桌面端('desktop')和手机端('mobile')的应用程序。
这张表的主键是 (user_id, spend_date, platform)。
平台列 platform 是一种 ENUM ,类型为('desktop', 'mobile')。
写一段 SQL 来查找每天 仅 使用手机端用户、仅 使用桌面端用户和 同时 使用桌面端和手机端的用户人数和总支出金额。
查询结果格式如下例所示:
Spending table:
+---------+------------+----------+--------+
| user_id | spend_date | platform | amount |
+---------+------------+----------+--------+
| 1 | 2019-07-01 | mobile | 100 |
| 1 | 2019-07-01 | desktop | 100 |
| 2 | 2019-07-01 | mobile | 100 |
| 2 | 2019-07-02 | mobile | 100 |
| 3 | 2019-07-01 | desktop | 100 |
| 3 | 2019-07-02 | desktop | 100 |
+---------+------------+----------+--------+
Result table:
+------------+----------+--------------+-------------+
| spend_date | platform | total_amount | total_users |
+------------+----------+--------------+-------------+
| 2019-07-01 | desktop | 100 | 1 |
| 2019-07-01 | mobile | 100 | 1 |
| 2019-07-01 | both | 200 | 1 |
| 2019-07-02 | desktop | 100 | 1 |
| 2019-07-02 | mobile | 100 | 1 |
| 2019-07-02 | both | 0 | 0 |
+------------+----------+--------------+-------------+
在 2019-07-01, 用户1 同时 使用桌面端和手机端购买, 用户2 仅 使用了手机端购买,而用户3 仅 使用了桌面端购买。
在 2019-07-02, 用户2 仅 使用了手机端购买, 用户3 仅 使用了桌面端购买,且没有用户 同时 使用桌面端和手机端购买。
生成数据
# 建表语句
CREATE TABLE Spending (
user_id INT,
spend_date DATE,
platform ENUM ('desktop', 'mobile'),
amount INT
) ;
# 插入测试数据
INSERT INTO Spending ( user_id, spend_date, platform, amount ) VALUES (1, '2019-07-01', 'mobile', 100) ;
INSERT INTO Spending ( user_id, spend_date, platform, amount ) VALUES (1, '2019-07-01', 'desktop', 100) ;
INSERT INTO Spending ( user_id, spend_date, platform, amount ) VALUES (2, '2019-07-01', 'mobile', 100) ;
INSERT INTO Spending ( user_id, spend_date, platform, amount ) VALUES (2, '2019-07-02', 'mobile', 100) ;
INSERT INTO Spending ( user_id, spend_date, platform, amount ) VALUES (3, '2019-07-01', 'desktop', 100) ;
INSERT INTO Spending ( user_id, spend_date, platform, amount ) VALUES (3, '2019-07-02', 'desktop', 100) ;
INSERT INTO Spending ( user_id, spend_date, platform, amount ) VALUES (3, '2019-07-03', 'desktop', 100) ;
解答
对user_id、 spend_date进行分组 统计 platform的数量
SELECT S.`user_id`, S.`spend_date`, COUNT(DISTINCT S.`platform`) AS num
FROM Spending AS S
GROUP BY S.`user_id`, S.`spend_date`;
数量超过两个的即为both 计算一下分组的amount和
SELECT S.`user_id`, S.`spend_date`,
IF(COUNT(DISTINCT S.`platform`) = 1, S.`platform`, 'both') AS platform,
SUM(S.`amount`) AS amount
FROM Spending AS S
GROUP BY S.`user_id`, S.`spend_date`;
对于上表 对时间和platform进行分组统计数量 得到 total_users
SELECT tmp.`spend_date`, tmp.`platform`, SUM(tmp.amount) AS amount, COUNT(tmp.user_id)
FROM (SELECT S.`user_id`, S.`spend_date`,
IF(COUNT(DISTINCT S.`platform`) = 1, S.`platform`, 'both') AS platform,
SUM(S.`amount`) AS amount
FROM Spending AS S
GROUP BY S.`user_id`, S.`spend_date`) AS tmp
GROUP BY tmp.`spend_date`, tmp.`platform`
但是题目要求不存在也要放在结果里面,只是结果置0而已。
一个想法就是把所有日期和三个类型的组合先写出来 这里用笛卡尔乘积即可
SELECT A.`spend_date`, B.`platform`
FROM (SELECT DISTINCT S.`spend_date`
FROM Spending AS S) AS A,
(SELECT 'desktop' AS platform UNION
SELECT 'mobile' AS platform UNION
SELECT 'both' AS platform
) AS B;
所有存在的结果和之前的结果进行左连接
SELECT all_res.spend_date, all_res.platform, res.amount, res.total_users
FROM (SELECT A.`spend_date`, B.`platform`
FROM (SELECT DISTINCT S.`spend_date`
FROM Spending AS S) AS A,
(SELECT 'desktop' AS platform UNION
SELECT 'mobile' AS platform UNION
SELECT 'both' AS platform
) AS B) AS all_res
LEFT JOIN (SELECT tmp.`spend_date`, tmp.`platform`, SUM(tmp.amount) AS amount, COUNT(tmp.user_id) AS total_users
FROM (SELECT S.`user_id`, S.`spend_date`,
IF(COUNT(DISTINCT S.`platform`) = 1, S.`platform`, 'both') AS platform,
SUM(S.`amount`) AS amount
FROM Spending AS S
GROUP BY S.`user_id`, S.`spend_date`) AS tmp
GROUP BY tmp.`spend_date`, tmp.`platform`) AS res
ON all_res.spend_date = res.spend_date AND all_res.platform = res.platform
将null转为0 即可
SELECT all_res.spend_date, all_res.platform, IFNULL(res.amount, 0) AS amount,
IFNULL(res.total_users,0) AS total_users
FROM (SELECT A.`spend_date`, B.`platform`
FROM (SELECT DISTINCT S.`spend_date`
FROM Spending AS S) AS A,
(SELECT 'desktop' AS platform UNION
SELECT 'mobile' AS platform UNION
SELECT 'both' AS platform
) AS B) AS all_res
LEFT JOIN (SELECT tmp.`spend_date`, tmp.`platform`, SUM(tmp.amount) AS amount, COUNT(tmp.user_id) AS total_users
FROM (SELECT S.`user_id`, S.`spend_date`,
IF(COUNT(DISTINCT S.`platform`) = 1, S.`platform`, 'both') AS platform,
SUM(S.`amount`) AS amount
FROM Spending AS S
GROUP BY S.`user_id`, S.`spend_date`) AS tmp
GROUP BY tmp.`spend_date`, tmp.`platform`) AS res
ON all_res.spend_date = res.spend_date AND all_res.platform = res.platform
GROUP BY all_res.spend_date, all_res.platform;
网友评论