行程取消率:先排除被封禁用户再算比例
LeetCode 262 · 条件聚合、过滤维表、比率计算
面试怎么回答
先过滤再聚合: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),否则比较会出错。