牛客SQL 大厂面试真题 某滴打车 6套代码及解析

4.出行场景(某滴打车)

SQL174 2021年国庆在北京接单3次及以上的司机统计信息

  1. where后面的时间一定要加上引号,否则会报不出结果。
    如本段代码中的where date(order_time) between ‘2021-10-1’ and ‘2021-10-7’,如果直接写成 2021-10-1 and 2021-10-7 而不是 ‘2021-10-1’ and ‘2021-10-7’ 就会没有结果。
  2. 题目中,接单被取消,也算入平均接单数。存在一个司机接单可能超过3单,但是3单都是取消的。因此不用使用 if 。。。 is null ,1,0 类似于这样的语句再进一步筛选
select
city,
round(avg(order_cnt),3) as avg_order_num,
round(avg(fare_cnt),3) as avg_income
from(
    select
    city,
    driver_id,
    count(order_id) as order_cnt,
    sum(fare) as fare_cnt
    from tb_get_car_order 
    join tb_get_car_record using(order_id)
    where date(order_time) between '2021-10-1' and '2021-10-7'
    and city='北京'
    group by driver_id
    having count(order_id)>=3
    ) as a
group by city

SQL175 有取消订单记录的司机平均评分

  1. 答案分成两部分,一个是筛选出的每个司机的情况,一个是总体情况。因此我们可以用一个表计算每个司机的情况,用一个表计算总体的情况,然后用union all 将两个表合并在一起。
  2. 要 求得 平均得分并不难,难点在筛选出订单取消的司机,这些司机没被取消的订单的平均得分。因此,我们可以在where条件语句里,再添加一个查询,筛选出符合在21年10月里订单被取消的司机的名单,然后再通过外面的查询语句计算平均得分
(
select
driver_id,
round(avg(grade),1) as avg_grade
from tb_get_car_order
join tb_get_car_record using(order_id)
where start_time is not null
    and driver_id in (
    select distinct driver_id 
    from tb_get_car_order
    where start_time is NULL 
    and date_format(order_time,'%Y-%m')='2021-10')
group by driver_id
order by driver_id
)
union all
(
select
'总体' as driver_id,
round(avg(grade),1) as avg_grade
from tb_get_car_order
join tb_get_car_record using(order_id)
where start_time is not null
    and driver_id in (
    select distinct driver_id 
    from tb_get_car_order
    where start_time is NULL
    and date_format(order_time,'%Y-%m')='2021-10')
)

SQL176 每个城市中评分最高的司机信息

  1. 最外层的是用来输出结果,第一个子查询用来排序,设置条件,第二个子查询用于计算各个指标。
  2. 题目要求“有多个司机评分并列最高时,都输出”,所以就不能使用先排序,再limit的方法了。可以使用dense_rank排序,在最后设置rank为1。
  3. 因为求得是日均接单量和日均行驶里程数,所以第二个子查询里面的order_time,要用date_foramat转化为天,并且需要用distinct进行去重,
    round(count(order_id)/count(distinct date_format(order_time,“%Y-%m-%d”)),1) as avg_order_num,
    round(sum(mileage)/count(distinct date_format(order_time,“%Y-%m-%d”)),3) as avg_mileage )
select 
city,
driver_id,
avg_grade,
avg_order_num,
avg_mileage
from (
    select 
    city,
    driver_id,
    avg_grade,
    avg_order_num,
    avg_mileage,
    dense_rank() over(partition by city order by avg_grade desc) as rank_grade
    from (
        select 
        city,
        driver_id,
        round(avg(grade),1)avg_grade,
        round(count(order_id)/count(distinct date_format(order_time,"%Y-%m-%d")),1) as avg_order_num,
        round(sum(mileage)/count(distinct date_format(order_time,"%Y-%m-%d")),3) as avg_mileage
        from tb_get_car_order
        left join tb_get_car_record using(order_id)
        group by city,driver_id
        )as a 
    )as b
where rank_grade=1
order by avg_order_num

SQL177 国庆期间近7日日均取消订单量

  1. 如果天数是连续的:
    也就是说9月25到10月3日每天都有订单,可以使用窗口函数,先对日期排序,然后取前六行和当前行。 rows between 2 preceding and current row
    如果天数不连续,中间有间断:
    例如9月26断了一天,那么统计10月2号的时候会把9月25号给统计进去,这个时候我们可以使用第一种方法。
  2. 第二个方法中 rows 6 preceding 表示的是 前面6行,如果不是每天都有订单,天数中断,那么这种方法就不可用了
select distinct date(order_time) dt,
       round(
           (select count(date(start_time))
            from tb_get_car_order t2
            where timestampdiff(day,date(order_time),date(t1.order_time)) between 0 and 6)/7
            ,2) as finish_num_7d,
       round(
           (select sum(if(start_time is null,1,0))
            from tb_get_car_order t3
            where timestampdiff(day,date(order_time),date(t1.order_time)) between 0 and 6)/7
            ,2) as cancel_num_7d
from tb_get_car_order t1
where date(order_time) between '2021-10-01' and '2021-10-03'
order by dt
select *
from (
    select dt,
    round(sum(finish_num) over (order by dt rows 6 preceding)/7,2) finish_num_7d,
    round(sum(cancel_num) over (order by dt rows 6 preceding)/7,2) cancel_num_7d
    from (
        select distinct date(order_time) as dt,
        count(start_time) as finish_num,
        sum(if(start_time is null,1,0)) as cancel_num
        from tb_get_car_order
        group by dt
        ) t1 
    ) t2
where dt between '2021-10-01' and '2021-10-03'
order by dt

SQL178 工作日各时段叫车量、等待接单时间和调整时间

  1. WEEKDAY()函数返回给定日期的工作日编号。
    注意: 0 =星期一,1 =星期二,2 =星期三,3 =星期四,4 =星期五,5 =星期六,6 =星期日。
  2. time(列名)表示时间(小时)
select 
(case when time(event_time)>='07:00:00' and time(event_time)<='09:00:00' then '早高峰'
when time(event_time)>='09:00:00' and time(event_time)<='17:00:00' then '工作时间' 
when time(event_time)>='17:00:00' and time(event_time)<='20:00:00' then '晚高峰'
else '休息时间' 
end ) as period,
count(order_time) as get_car_num,
round(avg(timestampdiff(second,event_time,order_time))/60,1) as avg_wait_time,
round(avg(timestampdiff(second,order_time,start_time))/60,1) as avg_dispatch_time
from tb_get_car_order
join tb_get_car_record using(order_id)
where weekday(event_time) between 0 and 4
group by period
order by get_car_num

SQL179 各城市最大同时等车人数

  1. 运用窗口函数对于不同城市的等车的人按照时间进行排序,得到同时等车人数,然后用max得到同时最多等车的人数。(窗口函数order by 具有累加的效果)
    整个解题过程只要3个步骤:
    首先,建立一张子表,对用户进入等车状态和离开等车状态进行定义后进行表并联;
    接着,使用窗口函数对每个城市的等车状态进出uv进行累加,
    最后,取出每个城市最大的UV即可。

  2. 用户分为以下三种状态:
    状态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 (ifnull(表达式1,表达式2):如果表达式1为空则返回表达式2,不为空则返回表达式1)
    定义完用户进入等车和离开等车这两种事件之后,关联所有表格,使用窗口函数排序累加即可。
    以上的三种状态,进入状态记作UV=1,离开状态记作UV=-1,

  3. 题目要求 “ 如果同一时刻有人停止等车,有人开始等车,等车人数记作先增加后减少。” 所以,第三行窗口函数对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一样按照城市升序

你可能感兴趣的:(SQL,sql,java,数据库)