订阅更新

用户购买平台:没有数据的组合也要显示 0

SQL 实战 123 题 · 第 65 题 困难 约 2 分钟读完

LeetCode 1127 · 补全维度组合、用户级打标签、LEFT JOIN

本文目录
  1. 面试怎么回答
  2. 题意
  3. 表结构
  4. 思路
  5. 参考解法
  6. 易错点

面试怎么回答

先在(日期, 用户)粒度打标签:COUNT(DISTINCT platform) = 1 就是该平台,否则是 both,并汇总当天金额;再用每个日期 × {desktop, mobile, both} 的组合表左关联标签结果,按日期和平台汇总,IFNULL 补 0。核心是先在用户粒度判断类别,再在结果粒度补全维度。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

题意

消费表记录用户每天在桌面端或手机端的消费金额。对每一天,分别统计「只用手机端」「只用桌面端」「两端都用」的用户数和总消费金额;某天某类没有用户时也要输出一行,金额和人数为 0。

表结构

  • Spending:user_id、spend_date、platform(desktop / mobile)、amount;(user_id, spend_date, platform) 为主键

思路

分三步:一是按(日期, 用户)分组,给每个用户当天打上平台标签:只用了一种平台就是那个平台,否则是 both,同时算出他当天的总金额;二是构造「每个日期 × 三种平台」的完整组合表;三是用组合表 LEFT JOIN 用户标签结果,再按日期和平台聚合,空值补 0。

参考解法

select
    spend_date, platform,
    ifnull(sum(total_am),0) total_amount,
    ifnull(sum(total_u),0) total_users
from
(
    select p.spend_date, p.platform, t.total_am, t.total_u
    from
    (
        select distinct spend_date, "desktop" platform from Spending
        union
        select distinct spend_date, "mobile" platform from Spending
        union
        select distinct spend_date, "both" platform from Spending
    ) p
    left join
    (
        select spend_date, 
            if(count(distinct platform)=1, platform, 'both') plat,
            sum(amount) total_am,
            count(distinct user_id) total_u
        from Spending
        group by spend_date, user_id
    ) t
    on p.platform = t.plat and p.spend_date = t.spend_date
) temp
group by spend_date, platform

易错点

  • 直接按平台分组,两端都用的用户会被分别计入 mobile 和 desktop,而不是 both。
  • 不补组合表的话,某天没有 both 用户时这一行就缺失了。
刷题进度保存在当前浏览器