LeetCode-1127. 用户购买平台(困难)-待深入理解

本文介绍了一种SQL查询方法,用于统计用户在不同平台(桌面端和移动端)的每日消费情况,包括仅使用单一平台和同时使用双平台的用户数量及总消费额。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

支出表: 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 仅 使用了桌面端购买,且没有用户 同时 使用桌面端和手机端购买。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/user-purchase-platform
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

审题:查找每天 仅 使用手机端用户、仅 使用桌面端用户和 同时 使用桌面端和手机端的用户人数和总支出金额。

思考:按照id和日期分组,如果有两个结果,就是都有,如果只有一个就是单一平台。

解题:

按照spend_date,user_id把只有desktop的,只有mobile的,以及both的全部查出来,

然后再把这个结果按照spend_date分组计算金额以及人数:

select spend_date,platform, sum(amount) as total_amount, count(user_id) total_users
from
(select spend_date, user_id, 
(case count(distinct platform)
    when 1 then platform
    when 2 then 'both'
    end
) as  platform, sum(amount) as amount
from Spending
group by spend_date, user_id
) as temp2
group by spend_date, platform

但是这样得出来的只有某些spend_date某些user_id
的,题目要求是不存在也要放在结果里面,只是结果置0而已。

::

UNION 操作符用于合并两个或多个 SELECT 语句的结果集。

请注意,UNION 内部的 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条 SELECT 语句中的列的顺序必须相同。

select distinct(spend_date), p.platform   
from Spending,
(select 'desktop' as platform union
 select 'mobile' as platform union
 select 'both' as platform
) as p 

这样枚举出来就把结果的前两列查出来了。
然后把这两步的结果合并就可以了。
完整的sql如下:

select temp1.spend_date, temp1.platform, 
       ifnull(temp3.total_amount, 0) total_amount, 
       ifnull(temp3.total_users,0) total_users
       from
(select distinct(spend_date), p.platform   
from Spending,
(select 'desktop' as platform union
 select 'mobile' as platform union
 select 'both' as platform
) as p 
) as temp1

left join 

(select spend_date,platform, sum(amount) as total_amount, count(user_id) total_users
from
(select spend_date, user_id, 
(case count(distinct platform)
    when 1 then platform
    when 2 then 'both'
    end
) as  platform, sum(amount) as amount
from Spending
group by spend_date, user_id
) as temp2
group by spend_date, platform
) as  temp3
on temp1.platform = temp3.platform and temp1.spend_date = temp3.spend_date

方法二:

第一步 观察样例
先观察样例,结果是按照时间和平台分组的,每个日期都对应了三个完整的平台,这种结构,是不是很像笛卡尔积呢(cross join)?
忘记的同学复习一下,假设集合A={a, b},集合B={0, 1, 2},则两个集合的笛卡尔积为{(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}。
因此,我们要是在时间和平台笛卡尔积基础上有了对应的实际数据,就可以进行统计了。
第二步 问题拆解
那么,我们的问题就可以拆解并分成以下几个步骤:
①原始表没有both的分类,我们加工下把他加上;
②最好有个完整的时间和分类表(笛卡尔积)
③我们把①表和②表left join一下,得到null值以方便进行0值的统计
④得到上述结果后,输出结论就是简单的group by操作了
第三步 分步操作得出结果
①:原始表加工,即创建新列platform和amount。(记录数6条——>5条)

select 
    a.user_id,a.spend_date,
    if(count(*)=1,a.platform,'both') as platform,   -- 根据同一天同用户的记录数,确定是否为'both'
    if(count(*)=1,a.amount,sum(a.amount)) as amount -- 同理,按条件分别输出amount与sum(amount)
from Spending a        -- 加a以作区分
group by user_id,spend_date

②:获取完整的时间和分类表。即时间和数组('desktop','mobile','both')的笛卡尔积。
1)这里由于不知道如何自定义一组数据作为一列,因此采用了2次union的方法,即以select 'desktop'作为单列单行数据,进行2次拼接,最终得到一个三行一列包含'desktop','mobile','both'的临时表a;
2)获取源数据中的完整日期信息,作临时表b
3)a,b取笛卡尔积。

select spend_date,platform from 
(select * from (select 'desktop' as platform) a union (select 'mobile') union (select 'both')) a,(select distinct spend_date from Spending) b

 ③+④:结果为按时间和平台分组,为了应对有些时间某平台记录为0,应以完整记录表left join出空值

select c.spend_date,c.platform,ifnull(sum(d.amount),0) as total_amount,count(d.user_id) as total_users
from 
    (select spend_date,platform from                    -- -------------------------
        (select * from (select 'desktop' as platform) b   -- 表②
        union (select 'mobile') union (select 'both')) e, -- |||||||||||||
        (select distinct spend_date from Spending) f) c  -- ------------------------
    left join                                            -- 左连接
    (select a.user_id,a.spend_date,                      -- ------------------------- 
        if(count(*)=1,a.platform,'both') as platform,    -- |||||||||||||
        if(count(*)=1,a.amount,sum(a.amount)) as amount  -- 表①
    from Spending a                                      -- |||||||||||||
    group by user_id,spend_date) d                       -- -------------------------
    on c.spend_date=d.spend_date and c.platform=d.platform -- 连接条件
group by c.spend_date,c.platform
order by field(c.platform,'desktop','mobile','both'),c.spend_date

 补充一点,为了自定义平台的排列顺序,可以用field函数。语法如下:field(数据来源列,自己写的顺序逗号分割)

知识点:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值