订阅更新

行程取消率:先排除被封禁用户再算比例

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

LeetCode 262 · 条件聚合、过滤维表、比率计算

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

面试怎么回答

先过滤再聚合:WHERE 里限定日期范围,并保证乘客和司机都不在封禁名单里;然后按日期分组,用 SUM(CASE WHEN Status != 'completed' THEN 1 ELSE 0 END) / COUNT(*) 算取消率,ROUND 保留两位。我更倾向用两次 JOIN 用户表来过滤,执行计划比 NOT IN 子查询直观,也不怕用户表里的 NULL。

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

详细讲解

题意

行程表记录了乘客、司机、日期和状态(完成、被司机取消、被乘客取消),用户表标记了用户是否被封禁。统计某三天里每天的取消率:只统计乘客和司机都没被封禁的行程,取消率 = 取消的行程数 / 总行程数,保留两位小数。

表结构

  • Trips:Id(主键)、Client_Id、Driver_Id、City_Id、Status(completed / cancelled_by_driver / cancelled_by_client)、Request_at
  • Users:Users_Id(主键)、Banned(Yes/No)、Role(client / driver / partner)

思路

分两步:先过滤出有效行程(时间在范围内,乘客和司机都未被封禁),再按日期分组,用条件聚合算取消数除以总数。原解法用 NOT IN 排除封禁用户;也可以把用户表关联两次(一次按乘客、一次按司机),关联时只取未封禁的用户。

参考解法

SELECT T.request_at AS `Day`, 
    ROUND(
            SUM(
                IF(T.STATUS = 'completed',0,1)
            )
            / 
            COUNT(T.STATUS),
            2
    ) AS `Cancellation Rate`
FROM trips AS T
WHERE 
T.Client_Id NOT IN (
    SELECT users_id
    FROM users
    WHERE banned = 'Yes'
)
AND
T.Driver_Id NOT IN (
    SELECT users_id
    FROM users
    WHERE banned = 'Yes'
)
AND T.request_at BETWEEN '2013-10-01' AND '2013-10-03'
GROUP BY T.request_at

易错点

  • 只排除了被封禁的乘客,忘了司机。
  • 把「被司机取消」和「被乘客取消」分开判断时漏掉一种;用「状态不是 completed」更不容易漏。
  • 日期是字符串时 BETWEEN 依赖格式统一(YYYY-MM-DD),否则比较会出错。
刷题进度保存在当前浏览器