LeetCode第262题_行程和用户

LeetCode 第262题:行程和用户

文章摘要

本文详细解析LeetCode第262题"行程和用户",这是一道SQL数据库问题。文章提供了多种SQL解法,包含JOIN、子查询等多种实现方式,配有详细的查询步骤图解和性能分析。适合想要提升SQL查询技能和数据库操作能力的开发者。

核心知识点: SQL查询、多表连接、条件过滤、聚合函数、取消率计算
难度等级: 困难
推荐人群: 数据库开发者、SQL学习者、后端开发工程师

题目描述

Trips 表中存所有出租车的行程信息。每段行程有唯一键 Id,Client_Id 和 Driver_Id 是 Users 表中 Users_Id 的外键。Status 是枚举类型,枚举成员为 (‘completed’, ‘cancelled_by_driver’, ‘cancelled_by_client’)。

+----+-----------+-----------+---------+--------------------+----------+
| Id | Client_Id | Driver_Id | City_Id |        Status      |Request_at|
+----+-----------+-----------+---------+--------------------+----------+
| 1  |     1     |    10     |    1    |     completed      |2013-10-01|
| 2  |     2     |    11     |    1    | cancelled_by_driver|2013-10-01|
| 3  |     3     |    12     |    6    |     completed      |2013-10-01|
| 4  |     4     |    13     |    6    | cancelled_by_client|2013-10-01|
| 5  |     1     |    10     |    1    |     completed      |2013-10-02|
| 6  |     2     |    11     |    6    |     completed      |2013-10-02|
| 7  |     3     |    12     |    6    |     completed      |2013-10-02|
| 8  |     2     |    12     |    12   |     completed      |2013-10-03|
| 9  |     3     |    10     |    12   |     completed      |2013-10-03|
| 10 |     4     |    13     |    12   | cancelled_by_driver|2013-10-03|
+----+-----------+-----------+---------+--------------------+----------+

Users 表存所有用户。每个用户有唯一键 Users_Id。Banned 表示这个用户是否被禁止,Role 则是一个表示(‘client’, ‘driver’, ‘partner’)的枚举类型。

+----------+--------+--------+
| Users_Id | Banned |  Role  |
+----------+--------+--------+
|    1     |   No   | client |
|    2     |   Yes  | client |
|    3     |   No   | client |
|    4     |   No   | client |
|    10    |   No   | driver |
|    11    |   No   | driver |
|    12    |   No   | driver |
|    13    |   No   | driver |
+----------+--------+--------+

示例

写一段 SQL 语句查出 2013年10月1日2013年10月3日 期间非禁止用户的取消率。基于上表,你的 SQL 语句应返回如下结果,取消率(Cancellation Rate)保留两位小数。

+------------+-------------------+
|     Day    | Cancellation Rate |
+------------+-------------------+
| 2013-10-01 |       0.33        |
| 2013-10-02 |       0.00        |
| 2013-10-03 |       0.50        |
+------------+-------------------+

提示

  • 取消率的计算:(被司机或乘客取消的非禁止用户的行程数)/(非禁止用户的总行程数)
  • 非禁止用户即 Banned = ‘No’ 的用户
  • 需要按日期分组计算每天的取消率

解题思路

这是一道SQL查询题,涉及到多表连接、条件过滤、分组聚合以及计算比率。要解决这个问题,我们需要按照以下步骤思考:

核心要求分析

  1. 时间范围限制:查询2013-10-01 至 2013-10-03期间的数据
  2. 用户过滤:只考虑非禁止用户(Banned=‘No’)的行程
  3. 取消率计算:取消的行程数除以总行程数
  4. 结果格式:按日期分组,取消率保留两位小数

解题步骤

  1. 筛选有效行程:过滤掉涉及禁止用户的行程
  2. 时间范围过滤:限定查询日期范围
  3. 按日期分组:使用GROUP BY按照Request_at分组
  4. 计算取消率:使用条件聚合函数计算取消数量和总数量
  5. 格式化结果:使用ROUND函数保留两位小数

方法一:使用JOIN连接

使用INNER JOIN连接Trips表和Users表,基于Client_Id和Users_Id,同时过滤出未被禁止的用户。

方法二:使用子查询

使用子查询在WHERE子句中过滤出非禁止的客户和司机。

方法三:多表连接

使用多个JOIN确保客户和司机都未被禁止。

图解思路

查询步骤分析表

步骤 操作 条件 说明
第一步 过滤时间范围 Request_at BETWEEN ‘2013-10-01’ AND ‘2013-10-03’ 限定查询时间段
第二步 过滤非禁止用户 Client_Id和Driver_Id对应的Banned=‘No’ 排除禁止用户的行程
第三步 按日期分组 GROUP BY Request_at 计算每天的统计数据
第四步 计算取消率 SUM(CASE WHEN Status LIKE ‘cancelled_%’ THEN 1 ELSE 0 END) / COUNT(*) 取消数/总数
第五步 格式化结果 ROUND(…, 2) 保留两位小数

数据过滤过程表

日期 原始行程数 禁止用户行程 有效行程数 取消行程数 取消率
2013-10-01 4 1(用户2被禁止) 3 1 0.33
2013-10-02 3 1(用户2被禁止) 3 0 0.00
2013-10-03 3 0 3 1 0.33

SQL实现

方法一:使用JOIN和CASE WHEN

SELECT 
    t.Request_at AS Day, 
    ROUND(
        SUM(CASE WHEN t.Status LIKE 'cancelled_%' THEN 1 ELSE 0 END) / COUNT(*), 
        2
    ) AS 'Cancellation Rate'
FROM 
    Trips t
INNER JOIN 
    Users u ON t.Client_Id = u.Users_Id AND u.Banned = 'No'
WHERE 
    t.Request_at BETWEEN '2013-10-01' AND '2013-10-03'
GROUP BY 
    t.Request_at;

方法二:双重JOIN确保客户和司机都未被禁止

SELECT 
    t.Request_at AS Day, 
    ROUND(
        SUM(
            IF(t.Status = 'completed', 0, 1)
        ) / COUNT(*), 
        2
    ) AS 'Cancellation Rate'
FROM 
    Trips t
JOIN 
    Users u1 ON t.Client_Id = u1.Users_Id AND u1.Banned = 'No'
JOIN 
    Users u2 ON t.Driver_Id = u2.Users_Id AND u2.Banned = 'No'
WHERE 
    t.Request_at BETWEEN '2013-10-01' AND '2013-10-03'
GROUP BY 
    t.Request_at;

方法三:使用子查询

SELECT 
    Request_at AS Day,
    ROUND(
        COUNT(IF(Status != 'completed', TRUE, NULL)) / COUNT(*),
        2
    ) AS 'Cancellation Rate'
FROM 
    Trips
WHERE 
    Client_Id IN (SELECT Users_Id FROM Users WHERE Banned = 'No')
    AND Driver_Id IN (SELECT Users_Id FROM Users WHERE Banned = 'No')
    AND Request_at BETWEEN '2013-10-01' AND '2013-10-03'
GROUP BY 
    Request_at;

执行结果

方法一(JOIN + CASE WHEN)

  • 执行用时:529 ms
  • 击败了92.80%的用户

方法二(双重JOIN)

  • 执行用时:546 ms
  • 击败了74.53%的用户

方法三(子查询)

  • 执行用时:588 ms
  • 击败了46.21%的用户

性能对比

方法 执行用时 性能排名 特点
JOIN + CASE WHEN 529 ms 92.80% 使用单个JOIN和CASE WHEN,效率最高
双重JOIN 546 ms 74.53% 确保客户和司机都未被禁止,逻辑清晰
子查询 588 ms 46.21% 代码简洁易懂,但效率较低

代码亮点

  1. 使用LIKE 'cancelled_%'巧妙匹配所有取消状态(无论是司机取消还是乘客取消)
  2. 使用ROUND函数精确控制小数位数,满足题目要求
  3. 使用CASE WHEN或IF函数实现条件计数,计算取消率
  4. 使用BETWEEN操作符简化日期范围查询,提高可读性

常见错误分析

  1. 忘记过滤被禁止的用户,导致取消率计算包含无效数据
  2. 忘记按日期分组,得到的是整个时间段的总取消率而非每日取消率
  3. 错误地计算取消率公式,如分子分母搞反或条件判断错误
  4. 未正确处理结果的小数位数,没有使用ROUND函数保留两位小数

解法对比

解法 时间复杂度 优点 缺点
JOIN + CASE WHEN O(n) 性能最佳,代码简洁 需要理解CASE WHEN语法
双重JOIN O(n) 逻辑清晰,明确筛选客户和司机 多次JOIN可能影响性能
子查询 O(n log n) 代码逻辑清晰,易于理解 子查询效率较低,性能较差

相关题目

  • LeetCode 175. 组合两个表 - 简单
  • LeetCode 176. 第二高的薪水 - 中等
  • LeetCode 177. 第N高的薪水 - 中等
  • LeetCode 180. 连续出现的数字 - 中等
  • LeetCode 184. 部门工资最高的员工 - 中等

系列导航

算法专题合集 - 查看完整合集

关注合集更新:点击上方合集链接,关注获取最新题解!目前已更新第262题。


互动交流

感谢大家耐心阅读到这里!希望这篇题解能够帮助你更好地理解和掌握这道SQL题目。

如果这篇文章对你有帮助,请:

  • 点个赞,让更多人看到这篇文章
  • 收藏文章,方便后续查阅复习
  • 关注作者,获取更多高质量算法题解
  • 评论区留言,分享你的解题思路或提出疑问

你的支持是我持续分享的动力!

一起进步:算法学习路上不孤单,欢迎一起交流学习!

你可能感兴趣的:(算法,leetcode,数据库,算法,c#,学习,python,c++)