Mysql日常实例 Demo_03

统计所有在线时间端的累计时长

有张车辆工作表,表结构如下

vehicle_id、video_status、type_time

车辆上安装了gps设备,设备一般30s上传一次数据报文, 报文中有动作类型字段 video_status, 1代表 车辆acc打开,也就是点火了,那么车辆就处于关闭状态,0代表 车辆acc关闭,也就是熄火。

现在想统计 每辆车当天 累计在线时长分别是多少

先贴代码

WITH StatusChanges AS (
    -- 标识每个状态变化的记录
    SELECT
        id,
		vehicle_id,
        case when video_status = 1 then '在线'
			 when video_status = 0 then '不在线'
		end as video_status,
        type_time,
		date_format(type_time,'%Y-%m-%d') as date,
        LAG( case when video_status = 1 then '在线'
				  when video_status = 0 then '不在线'
			 end ) 
		OVER (PARTITION BY vehicle_id ORDER BY type_time desc) AS prev_status
    FROM ty_vehicle_card_info_01 
	where type_time <CURRENT_TIME
	and   vehicle_id = 6 
	and date_format(type_time,'%Y-%m-%d') = '2024-10-23'

),
OnlinePeriods AS (
    -- 标识每个在线时间段的开始和结束
    SELECT
        id,
		vehicle_id,
        video_status,
		prev_status,
        type_time,
		date,
        SUM(CASE WHEN video_status = '在线' AND prev_status = '不在线' THEN 1 ELSE 0 END) OVER (PARTITION BY vehicle_id ORDER BY type_time desc) AS  online_group
    FROM StatusChanges
		 
),
OnlineIntervals AS (
    -- 计算每个在线时间段的持续时间
    SELECT
		vehicle_id,
		date,
        online_group,
        MIN(type_time) AS start_time,
        MAX(type_time) AS end_time
    FROM OnlinePeriods
    WHERE video_status = '在线'
    GROUP BY vehicle_id,date,online_group
)
-- 汇总所有在线时间段的总时长,
,
sum_cte as (
SELECT
    vehicle_id,
	date,
    SUM(TIMESTAMPDIFF(minute, start_time, end_time)) AS total_online_duration
FROM OnlineIntervals
GROUP BY vehicle_id, date 
ORDER BY vehicle_id, date desc
)
select *  
from OnlineIntervals;

原理

这个sql里面有两个比较重要的知识点,一个是窗口函数,一个是两个窗口的两个实例 lag over和sum over,窗口函数中的over相当于group by , over里面的partition by 字段其实就相当于group by字段的意思,

这里的 LAG 函数会获取上一行的 video_status 值,并在当前行创建一个新的列 prev_status,用于保存上一行的状态
sum就不多说了,只不过需要注意的是 窗口函数的计算是逐行机型的,每一行都会累积之前符合条件的值,和普通的聚合函数有点区别!!!

现在让我们一步步来解析sql具体发生了啥,以及最后是怎么统计出所有在线时间段有哪些,并且最终如何计算总的在线时间段时长的。

  1. StatusChanges
    首先 这个用于获取所有记录的上一条记录的video_status,记做prev_status,这里为了查看方便我就稍做了处理,在lag里面做了case when 的处理。得到的结果如下
    在这里插入图片描述
    由于数据有点多,这里截不全,最后我会把查到的真实数据文件发出来,方便理解。

  2. OnlinePeriods
    在这里插入图片描述
    这里也是截取了部分数据,我截取了当在线状态发生变化的那个时候,记录的几个字段发生的变化,可以看到这个时候online_group发生了变化,因为online_group的逻辑是

SUM(CASE WHEN video_status = '在线' AND prev_status = '不在线' THEN 1 ELSE 0 END) OVER (PARTITION BY vehicle_id ORDER BY type_time desc) AS  online_group

当前状态是在线并且前一条状态是不在线 那么才累加1,由于前面的online_group值是0,所以第一次状态由不在线切换到在线的时候,online_group从0变为1 ,而后面由于没有满足条件,所以后续值一直都是1 ,直到下一个 满足sum条件的记录出现。
4. OnlineIntervals
注意在这个sql中 添加了WHERE video_status = '在线' 的条件过滤, 这样就把所有在线的记录晒出来了,因为不添加where条件的话,当遇到不满足sum条件的记录的时候,online_group的值仍然和上面一条满足条件的online_group相同,这样后面根据online_group分组就会出现问题,添加了where条件之后 然后由于又按照online_group分组了,那么不同在线时段也就被区分开来了,
由于数据较多,中间的过程数据我这里不能贴出来,附件中我用不同颜色区分出了不同online_group分组,方便理解,我截一下最后的结果数据
在这里插入图片描述
红色标注的就是不同在线时段的分组号

  1. sum_cte
    不同在线时段已经筛选出来了,那么剩下的就是简单的求和了,这个我就不多说了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值