一、知识点总结与拓展
感觉本题主要的考点是对瞬时在线用户数这种业务的理解。用到SQL的方法相对简单,只要是union的用法,除此之外还用大了ifnull()函数。
- union 去重连接;union all 全连接不去重
- ifnull(表达式1,表达式2):如果表达式1为空则返回表达式2,不为空则返回表达式1
- 瞬时UV的计算方法:进入事件记作UV=1,离开事件记作UV=-1,使用窗口函数SUM(uv)OVER(ORDER BY 事件,uv DESC)进行累加。
二、题目解读
题目:请统计各个城市在2021年10月期间,单日中最大的同时等车人数。
这是一道典型的统计同时在线用户数的题,和统计直播间在线用户数的逻辑是一样的。进入的用户定义uv为1,离开的用户定义uv为-1。
因而,需要对用户进入和离开的时间进行定义。
进入时间:event_time,开始打车的时间即为等车开始。
离开时间:有3种情况
- 状态1:司机接单前取消,则没有生成order_id,这种情况 order_id IS NULL 记录end_time
- 状态2:司机接单后取消,则没有上车时间,start_time IS NULL 记录 finish_time
- 状态3:正常上车,记录start_time,start_time IS NOT NULL
- 状态2和3可以直接使用IFNULL()合并,IFNULL(start_time,finish_time) 如果start_time空则返回finish_time,不空则start_time
定义完用户进入等车和离开等车这两种事件之后,关联所有表格,使用窗口函数排序累加即可。
- 具体的状态的数据可以看下面的图
三、解题步骤
解题思路:整个解题过程只要3个步骤,首先建立一张子表,对用户进入等车状态和离开等车状态进行定义后进行表并联;接着使用窗口函数对每个城市的等车状态进出uv进行累加,最后取出每个城市最大的UV即可。
1)建立子表,对用户进入等车状态和离开等车状态进行定义后进行表并联
- uv为1,用户进入打车状态
SELECT city,event_time uv_time,1 AS uv FROM tb_get_car_record
- uv为-1,状态1:司机接单前取消,则没有生成order_id,这种情况 order_id IS NULL 记录end_time
SELECT city,end_time uv_time,-1 AS uv FROM tb_get_car_record WHERE order_id IS NULL #接单前取消
- uv为-1,状态2:接单后取消或者用户正常上车
SELECT city,IFNULL(start_time,finish_time) uv_time,-1 AS uv FROM tb_get_car_order LEFT JOIN tb_get_car_record USING(order_id)#接单后取消或上车
- 建立子表:使用union all 全关联
SELECT city,event_time uv_time,1 AS uv FROM tb_get_car_record UNION ALL SELECT city,end_time uv_time,-1 AS uv FROM tb_get_car_record WHERE order_id IS NULL #接单前取消 UNION ALL SELECT city,IFNULL(start_time,finish_time) uv_time,-1 AS uv FROM tb_get_car_order LEFT JOIN tb_get_car_record USING(order_id)#接单后取消或上车
2)使用窗口函数对每个城市的等车状态进出uv进行累加
- 统计2021年10月,每个城市的瞬时UV情况
SELECT city,SUM(uv)OVER(PARTITION BY city ORDER BY uv_time,uv DESC) AS uv_cnt
DATE_FORMAT(uv_time,'%Y%m')='202110'
3)最后取出每个城市最大的UV,排序先按照uv升序,uv一样按照城市升序。
- 完整代码
WITH t1 AS( SELECT city,SUM(uv)OVER(PARTITION BY city ORDER BY uv_time,uv DESC) AS uv_cnt #每个城市等车瞬时UV FROM ( SELECT city,event_time uv_time,1 AS uv FROM tb_get_car_record #进入等车状态 UNION ALL SELECT city,end_time uv_time,-1 AS uv FROM tb_get_car_record WHERE order_id IS NULL #接单前取消 UNION ALL SELECT city,IFNULL(start_time,finish_time) uv_time,-1 AS uv FROM tb_get_car_order LEFT JOIN tb_get_car_record USING(order_id)#接单后取消或上车 )AS t WHERE DATE_FORMAT(uv_time,'%Y%m')='202110' #2021年10月 ) SELECT city,MAX(uv_cnt) max_wait_uv FROM t1 GROUP BY citY ORDER BY max_wait_uv,citY;#排序先按照uv升序,uv一样按照城市升序