横扫SQL面试——时间序列分组与合并(会话划分)问题

横扫SQL面试题

📌 时间序列分组与合并问题

在这里插入图片描述

📚 横扫SQL面试——时间序列分组与合并解析


🌟 核心问题类型

时间序列分组(Sessionization)
处理具有时间维度的连续数据流,根据特定规则(如时间间隔、属性连续性)将相邻记录合并为逻辑组


话不多说——直接上题:🎈🎈🎈🎈🎈🎈🎈

一、用户访问记录会话划分🎈🎈🎈

给定用户的访问记录access_records表,需要按照同一用户相邻两次访问记录时间间隔小于等于60秒则认为两次浏览记录属于同一会话的规则,为属于同一会话的访问记录增加一个相同的会话id字段。

表名字段名字段含义数据类型
用户访问记录表user_id用户标识VARCHAR
access_time访问时间DATETIME
action访问时执行的操作(如页面浏览等)VARCHAR
user_idaccess_timeaction
User12025-03-31 10:00:00浏览首页
User12025-03-31 10:00:30点击商品详情
User22025-03-31 10:10:00浏览列表页
User12025-03-31 10:01:00返回首页
User22025-03-31 10:10:30加入购物车

结果预期

user_idaccess_timeactionsession_id
User12025-03-31 10:00:00浏览首页1
User12025-03-31 10:00:30点击商品详情1
User12025-03-31 10:01:00返回首页1
User22025-03-31 10:10:00浏览列表页2
User22025-03-31 10:10:30加入购物车2

✅✅✅

原始数据 → 计算相邻时间差 → 标记新会话 → 累加分组 → 分配ID↓              ↓               ↓             ↓
prev_access_time → new_session_start → session_groups → 最终结果
步骤1:获取相邻记录时间差(CTE: prev_access_time)✅
with prev_access_time as (selectuser_id,access_time,lag(access_time) over (partition by user_id order by access_time) as prev_timefrom access_records
)

在这里插入图片描述

在这里插入图片描述

输入数据

user_idaccess_time
UserA2024-01-01 08:00:00
UserA2024-01-01 08:00:50
UserA2024-01-01 08:02:00

中间表 prev_access_time

user_idaccess_timeprev_time
UserA2024-01-01 08:00:00NULL
UserA2024-01-01 08:00:502024-01-01 08:00:00
UserA2024-01-01 08:02:002024-01-01 08:00:50
  • LAG(access_time) 获取同一用户前一条记录的访问时间
  • PARTITION BY user_id 确保按用户独立处理
  • ORDER BY access_time 按时间顺序排序

步骤2:标记新会话起点(CTE: new_session_start)✅

在这里插入图片描述

new_session_start as (selectuser_id,access_time,casewhen timestampdiff(second, prev_time, access_time) >= 60 or prev_time is nullthen 1else 0end as is_new_sessionfrom prev_access_time
)

在这里插入图片描述

中间表 new_session_start

user_idaccess_timeis_new_session
UserA2024-01-01 08:00:001 (首条记录)
UserA2024-01-01 08:00:500 (间隔50秒)
UserA2024-01-01 08:02:001 (间隔70秒)

判断逻辑

  • 首条记录:prev_time IS NULL → 标记为1
  • 间隔计算:08:00:50 - 08:00:00 = 50秒<60秒 → 标记0
  • 间隔计算:08:02:00 - 08:00:50 = 70秒>=60秒 → 标记1

步骤3:生成会话分组(CTE: session_groups)✅
session_groups as (selectuser_id,access_time,sum(is_new_session) over (partition by user_id order by access_time) as session_groupfrom new_session_start
)

在这里插入图片描述

在这里插入图片描述

中间表 session_groups

user_idaccess_timesession_group
UserA2024-01-01 08:00:001
UserA2024-01-01 08:00:501 (累加0)
UserA2024-01-01 08:02:002 (累加1)
  • SUM(is_new_session) 按用户和时间顺序累加标记
  • 同一会话组内的记录具有相同的 session_group

步骤4:分配会话ID(主查询)✅
selectuser_id,access_time,dense_rank() over (partition by user_id order by session_group) as session_id
from session_groups;

在这里插入图片描述

在这里插入图片描述

最终结果

user_idaccess_timesession_id
UserA2024-01-01 08:00:001
UserA2024-01-01 08:00:501
UserA2024-01-01 08:02:002
  • DENSE_RANK() 保证会话ID连续(无间隔)
  • session_group 排序确保同一组ID相同

二、网站页面浏览时长统计🎈🎈🎈

给定网站页面浏览记录,需要按照用户在离开页面后30分钟内再次访问同一页面则认为是连续浏览的规则
计算:

  1. 合并时长:将连续访问合并为一个时间段,计算从首次开始时间到最后结束时间的总跨度
  2. 实际时长累加每次独立访问的实际时长(不合并连续访问)
  3. 增加会话id:属于同一浏览的访问记录增加一个相同的会话id字段。
表名字段名字段含义数据类型
网站页面浏览记录表user_id用户标识VARCHAR
page_url页面URLVARCHAR
view_start_time开始浏览时间DATETIME
view_end_time结束浏览时间DATETIME
user_idpage_urlview_start_timeview_end_time
User1https://example.com2025-04-01 09:00:002025-04-01 09:10:00
User1https://example.com2025-04-01 09:15:002025-04-01 09:20:00
User2https://example.net2025-04-01 10:00:002025-04-01 10:15:00
User1https://example.com2025-04-01 09:25:002025-04-01 09:30:00
User2https://example.net2025-04-01 10:30:002025-04-01 10:45:00

结果预期

user_idpage_urlview_start_timeview_end_timemerged_durationtotal_real_duration
User1https://example.com2025-04-01 09:00:002025-04-01 09:30:003020 (10+5+5)
User2https://example.net2025-04-01 10:00:002025-04-01 10:15:001515
User2https://example.net2025-04-01 10:30:002025-04-01 10:45:001515

还是跟上题同样的套路~😝😝😝

步骤核心操作关键函数/逻辑
时间排序获取前次结束时间LAG() + 窗口排序
标记会话起点判断30分钟间隔TIMESTAMPDIFF() + CASE
生成会话组累加标记值SUM() OVER()
分配会话ID生成唯一连续编号DENSE_RANK()
聚合结果计算时间跨度和累加时长MIN()/MAX()/SUM()
步骤1:排序并获取前次结束时间
with ordered_views as(selectuser_id,page_url,view_start_time,view_end_time,lag(view_end_time) over(partition by user_id,page_url order by view_start_time) as prev_end_timefrom table
)

在这里插入图片描述

中间表 ordered_views

user_idpage_urlview_start_timeview_end_timeprev_end_time
User1https://example.com2025-04-01 09:00:002025-04-01 09:10:00NULL
User1https://example.com2025-04-01 09:15:002025-04-01 09:20:002025-04-01 09:10:00
User1https://example.com2025-04-01 09:25:002025-04-01 09:30:002025-04-01 09:20:00
User2https://example.net2025-04-01 10:00:002025-04-01 10:15:00NULL
User2https://example.net2025-04-01 10:30:002025-04-01 10:45:002025-04-01 10:15:00

步骤2:标记新会话起点
session_flags as(select *,casewhen prev_end_time IS NULL OR timestampdiff(minute, prev_end_time, view_start_time) > 30 then 1 else 0 end as is_new_sessionfrom ordered_views
)

在这里插入图片描述

中间表 session_flags

user_idpage_urlis_new_session
User1https://example.com1
User1https://example.com0
User1https://example.com0
User2https://example.net1
User2https://example.net1

步骤3:生成会话分组ID
session_groups as(select*,sum(is_new_session) over(partition by user_id, page_url order by view_start_time) as session_groupfrom session_flags
)

在这里插入图片描述

中间表 session_groups

user_idpage_urlsession_group
User1https://example.com1
User1https://example.com1
User1https://example.com1
User2https://example.net1
User2https://example.net2

步骤4:分配唯一会话ID
session_ids AS (SELECT *,dense_rank() over (partition by user_id, page_url order by session_group ) as session_idfrom session_groups
)

在这里插入图片描述

中间表 session_ids

user_idpage_urlsession_id
User1https://example.com1
User1https://example.com1
User1https://example.com1
User2https://example.net1
User2https://example.net2

步骤5:聚合结果并计算双指标
selectuser_id,page_url,session_id,min(view_start_time) AS view_start_time,max(view_end_time) AS view_end_time,TIMESTAMPDIFF(MINUTE, min(view_start_time), max(view_end_time)) AS merged_duration,sum(TIMESTAMPDIFF(MINUTE, view_start_time, view_end_time)) AS total_real_duration
from session_ids
group by user_id, page_url, session_id;

在这里插入图片描述

最终结果

user_idpage_urlsession_idview_start_timeview_end_timemerged_durationtotal_real_duration
User1https://example.com12025-04-01 09:00:002025-04-01 09:30:003020
User2https://example.net12025-04-01 10:00:002025-04-01 10:15:001515
User2https://example.net22025-04-01 10:30:002025-04-01 10:45:001515

还是有些难度的哈~

-- 使用CTE(公共表表达式),命名为ordered_views
with ordered_views as (-- 从“网站页面浏览记录表”中选择数据select user_id,page_url,view_start_time,view_end_time,-- 使用LAG窗口函数,获取每个用户和页面URL分组下,按浏览开始时间排序后的上一条记录的浏览结束时间lag(view_end_time) over (partition by user_id, page_url order by view_start_time) as prev_end_timefrom 网站页面浏览记录表
)
-- 第二个CTE,命名为session_flags
, session_flags as (-- 选择ordered_views CTE中的所有列,并添加一个新列is_new_sessionselect *,-- 使用CASE语句判断是否为新会话case -- 如果上一条记录的结束时间为空(即当前是该用户和页面的第一条记录)-- 或者当前记录的开始时间与上一条记录的结束时间间隔大于30分钟when prev_end_time is null or timestampdiff(minute, prev_end_time, view_start_time) > 30 then 1 else 0 end as is_new_sessionfrom ordered_views
)
-- 第三个CTE,命名为session_groups
, session_groups as (-- 选择session_flags CTE中的所有列,并添加一个新列session_groupselect *,-- 使用SUM窗口函数,对每个用户和页面URL分组下,按浏览开始时间排序后的is_new_session列进行累加sum(is_new_session) over (partition by user_id, page_url order by view_start_time) as session_groupfrom session_flags
)
-- 第四个CTE,命名为session_ids
, session_ids as (-- 选择session_groups CTE中的所有列,并添加一个新列session_idselect *,-- 使用DENSE_RANK窗口函数,对每个用户和页面URL分组下,按session_group进行排名,得到会话IDdense_rank() over (partition by user_id, page_url order by session_group) as session_idfrom session_groups
)-- 主查询
select user_id,page_url,session_id,-- 对于每个用户、页面和会话ID分组,获取最小的浏览开始时间min(view_start_time) as view_start_time,-- 对于每个用户、页面和会话ID分组,获取最大的浏览结束时间max(view_end_time) as view_end_time,-- 计算合并后的会话时长(以分钟为单位)timestampdiff(minute, min(view_start_time), max(view_end_time)) as merged_duration,-- 计算每个会话内实际的浏览时长总和(以分钟为单位)sum(timestampdiff(minute, view_start_time, view_end_time)) as total_real_duration
from session_ids
-- 按照用户ID、页面URL和会话ID进行分组
group by user_id, page_url, session_id
-- 按照用户ID、页面URL和会话ID进行排序
order by user_id, page_url, session_id;

三、公交乘客乘车时间分析🎈🎈🎈

根据公交乘客乘车记录,按照 “下车后90分钟内再次上车视为同一次出行” 的规则,完成以下任务:

  1. 分配出行ID:为每个乘客的每次连续出行分配唯一ID
  2. 计算双指标
    • 合并时长:从首次上车到最后一次下车的总时间跨度
    • 实际时长:累加各次乘车的独立时长
表名字段名字段含义数据类型
公交乘车记录表passenger_id乘客标识VARCHAR
boarding_time上车时间DATETIME
alighting_time下车时间DATETIME

passenger_idboarding_timealighting_time
P0012025-04-01 08:00:002025-04-01 08:30:00
P0012025-04-01 08:40:002025-04-01 09:00:00
P0022025-04-01 09:30:002025-04-01 10:00:00
P0012025-04-01 09:10:002025-04-01 09:30:00
P0022025-04-01 10:40:002025-04-01 11:00:00

结果预期

passenger_idtrip_idboarding_timealighting_timemerged_durationtotal_actual_duration
P00112025-04-01 08:00:002025-04-01 09:30:009070 (30+20+20)
P00212025-04-01 09:30:002025-04-01 10:00:003030
P00222025-04-01 10:40:002025-04-01 11:00:002020

换汤不换药~😂


步骤1:排序并获取前次下车时间

在这里插入图片描述

中间表 ordered_records

passenger_idboarding_timealighting_timeprev_alighting_time
P0012025-04-01 08:00:002025-04-01 08:30:00NULL
P0012025-04-01 08:40:002025-04-01 09:00:002025-04-01 08:30:00
P0012025-04-01 09:10:002025-04-01 09:30:002025-04-01 09:00:00
P0022025-04-01 09:30:002025-04-01 10:00:00NULL
P0022025-04-01 10:40:002025-04-01 11:00:002025-04-01 10:00:00

步骤2:标记新出行起点

在这里插入图片描述

中间表 session_flags

passenger_idis_new_session
P0011 (首条记录)
P0010 (间隔10分钟)
P0010 (间隔10分钟)
P0021
P0021 (间隔40分钟)

步骤3:生成出行分组ID

在这里插入图片描述

中间表 session_groups

passenger_idsession_group
P0011
P0011
P0011
P0021
P0022

步骤4:分配出行ID并聚合结果

在这里插入图片描述

最终结果

passenger_idboarding_timealighting_timetotal_riding_durationtotal_actual_duration
P0012025-04-01 08:00:002025-04-01 09:30:009070 (30+20+20)
P0022025-04-01 09:30:002025-04-01 10:00:003030
P0022025-04-01 10:40:002025-04-01 11:00:002020

-- 使用CTE(公共表表达式),命名为ordered_records
with ordered_records as (-- 从“公交乘车记录表”中选择数据select passenger_id,boarding_time,alighting_time,-- 使用LAG窗口函数,获取每个乘客分组下,按上车时间排序后的上一条记录的下车时间lag(alighting_time) over (partition by passenger_id order by boarding_time) as prev_alighting_timefrom 公交乘车记录表
)
-- 第二个CTE,命名为session_flags
, session_flags as (-- 选择ordered_records CTE中的所有列,并添加一个新列is_new_sessionselect *,-- 使用CASE语句判断是否为新会话case -- 如果上一条记录的下车时间为空(即当前是该乘客的第一条记录)-- 或者当前记录的上车时间与上一条记录的下车时间间隔大于90分钟when prev_alighting_time is null or timestampdiff(minute, prev_alighting_time, boarding_time) > 90 then 1 else 0 end as is_new_sessionfrom ordered_records
)
-- 第三个CTE,命名为session_groups
, session_groups as (-- 选择session_flags CTE中的所有列,并添加一个新列session_groupselect *,-- 使用SUM窗口函数,对每个乘客分组下,按上车时间排序后的is_new_session列进行累加sum(is_new_session) over (partition by passenger_id order by boarding_time) as session_groupfrom session_flags
)-- 主查询
select passenger_id,-- 对于每个乘客和会话组分组,获取最小的上车时间min(boarding_time) as boarding_time,-- 对于每个乘客和会话组分组,获取最大的下车时间max(alighting_time) as alighting_time,-- 计算总的乘车时长(以分钟为单位)timestampdiff(minute, min(boarding_time), max(alighting_time)) as total_riding_duration,-- 计算每个会话组内实际的乘车时长总和(以分钟为单位)sum(timestampdiff(minute, boarding_time, alighting_time)) as total_actual_duration
from session_groups
-- 按照乘客ID和会话组进行分组
group by passenger_id, session_group
-- 按照乘客ID和会话组进行排序
order by passenger_id, session_group;

熟悉的套路😂

步骤核心操作关键函数/逻辑
数据排序按乘客和时间排序LAG() + 窗口排序
标记新出行起点判断90分钟间隔TIMESTAMPDIFF() + CASE
生成分组ID累加标记值SUM() OVER()
聚合结果计算时间跨度和实际时长MIN()/MAX()/SUM()

后面大家可以自己做一做 博主就不啰嗦啦~ 答案贴在最后啦 做完再看哈🤣

📚 时间序列分组与合并扩展题库


题目一:用户登录会话合并

题干
合并用户登录记录,若两次登录间隔 ≤15分钟 视为同一会话,计算每个会话的持续时间和登录次数。

表结构

表名字段名字段含义数据类型
user_loginuser_id用户IDVARCHAR
login_time登录时间DATETIME

示例数据

user_idlogin_time
U10012024-03-01 08:00:00
U10012024-03-01 08:05:00
U10012024-03-01 08:25:00

期望输出

user_idsession_startsession_endduration_minlogin_count
U10012024-03-01 08:00:002024-03-01 08:05:0052
U10012024-03-01 08:25:002024-03-01 08:25:0001

题目二:设备故障时段统计

题干
合并设备的连续故障时段(故障状态代码=500),若两次故障间隔 ≤10分钟 视为同一故障事件。

表结构

表名字段名字段含义数据类型
device_logdevice_id设备IDVARCHAR
log_time日志时间DATETIME
status_code状态码INT

示例数据

device_idlog_timestatus_code
D0012024-03-01 10:00:00500
D0012024-03-01 10:05:00500
D0012024-03-01 10:20:00500

期望输出

device_idstart_timeend_timeerror_duration_min
D0012024-03-01 10:00:002024-03-01 10:05:005
D0012024-03-01 10:20:002024-03-01 10:20:000

题目三:订单支付超时分析

题干
合并订单的创建和支付操作,若支付时间在创建后 30分钟内 视为有效支付,统计每个订单的实际支付处理时长。

表结构

表名字段名字段含义数据类型
ordersorder_id订单IDVARCHAR
create_time创建时间DATETIME
pay_time支付时间DATETIME

示例数据

order_idcreate_timepay_time
O10012024-03-01 12:00:002024-03-01 12:05:00
O10012024-03-01 12:10:002024-03-01 12:30:00
O10022024-03-01 13:00:00NULL

期望输出

order_idvalid_pay_duration_min
O100125 (12:05-12:30)
O10020

题目四:交通卡口连续通行分析

题干
合并车辆通过相邻卡口的记录,若通过时间间隔 ≤5分钟 且行驶方向相同,视为连续通行,计算平均速度。

表结构

表名字段名字段含义数据类型
traffic_logcar_plate车牌号VARCHAR
direction行驶方向VARCHAR
pass_time通过时间DATETIME
location卡口位置VARCHAR

示例数据

car_platedirectionpass_timelocation
沪A12345东向2024-03-01 09:00:00卡口A
沪A12345东向2024-03-01 09:03:00卡口B
沪A12345东向2024-03-01 09:10:00卡口C

期望输出

car_platestart_timeend_timeavg_speed_kmh
沪A123452024-03-01 09:00:002024-03-01 09:10:0048

答案:
题目一:用户登录会话合并
-- 使用公共表表达式(CTE),命名为 ordered_logins
with ordered_logins as (-- 从 user_login 表中选择数据select user_id,login_time,-- 使用 LAG 窗口函数,在每个 user_id 分区内,按照 login_time 排序-- 获取当前记录的上一条记录的 login_time,命名为 prev_loginlag(login_time) over (partition by user_id order by login_time) as prev_loginfrom user_login
)
-- 第二个 CTE,命名为 session_flags
, session_flags as (-- 选择 ordered_logins CTE 中的所有列,并添加一个新列 is_new_sessionselect *,-- 使用 CASE 语句判断是否为新的会话case -- 如果上一条记录的登录时间为空(即当前是该用户的第一条登录记录)-- 或者当前记录的登录时间与上一条记录的登录时间间隔大于 15 分钟when prev_login is null or timestampdiff(minute, prev_login, login_time) > 15 then 1 else 0 end as is_new_sessionfrom ordered_logins
)
-- 第三个 CTE,命名为 session_groups
, session_groups as (-- 选择 session_flags CTE 中的所有列,并添加一个新列 session_idselect *,-- 使用 SUM 窗口函数,在每个 user_id 分区内,按照 login_time 排序-- 对 is_new_session 列进行累加,得到每个会话的唯一标识 session_idsum(is_new_session) over (partition by user_id order by login_time) as session_idfrom session_flags
)
-- 主查询
select user_id,-- 对于每个用户和会话 ID 分组,获取最小的登录时间作为会话开始时间min(login_time) as session_start,-- 对于每个用户和会话 ID 分组,获取最大的登录时间作为会话结束时间max(login_time) as session_end,-- 计算会话的持续时间(以分钟为单位)timestampdiff(minute, min(login_time), max(login_time)) as duration_min,-- 统计每个会话内的登录次数count(*) as login_count
from session_groups
-- 按照用户 ID 和会话 ID 进行分组
group by user_id, session_id;
题目二:设备故障时段统计
-- 使用公共表表达式(CTE),命名为 ordered_logs
with ordered_logs as (-- 从 device_log 表中选择数据,且只选择状态码为 500 的记录select device_id,log_time,-- 使用 LAG 窗口函数,在每个 device_id 分区内,按照 log_time 排序-- 获取当前记录的上一条记录的 log_time,命名为 prev_loglag(log_time) over (partition by device_id order by log_time) as prev_logfrom device_logwhere status_code = 500
)
-- 第二个 CTE,命名为 session_flags
, session_flags as (-- 选择 ordered_logs CTE 中的所有列,并添加一个新列 is_new_sessionselect *,-- 使用 CASE 语句判断是否为新的故障时段case -- 如果上一条记录的日志时间为空(即当前是该设备的第一条故障记录)-- 或者当前记录的日志时间与上一条记录的日志时间间隔大于 10 分钟when prev_log is null or timestampdiff(minute, prev_log, log_time) > 10 then 1 else 0 end as is_new_sessionfrom ordered_logs
)
-- 第三个 CTE,命名为 session_groups
, session_groups as (-- 选择 session_flags CTE 中的所有列,并添加一个新列 session_idselect *,-- 使用 SUM 窗口函数,在每个 device_id 分区内,按照 log_time 排序-- 对 is_new_session 列进行累加,得到每个故障时段的唯一标识 session_idsum(is_new_session) over (partition by device_id order by log_time) as session_idfrom session_flags
)
-- 主查询
select device_id,-- 对于每个设备和故障时段 ID 分组,获取最小的日志时间作为故障开始时间min(log_time) as start_time,-- 对于每个设备和故障时段 ID 分组,获取最大的日志时间作为故障结束时间max(log_time) as end_time,-- 计算故障时段的持续时间(以分钟为单位)timestampdiff(minute, min(log_time), max(log_time)) as error_duration_min
from session_groups
-- 按照设备 ID 和故障时段 ID 进行分组
group by device_id, session_id;
题目三:订单支付超时分析
select order_id,-- 使用 COALESCE 函数,若第一个参数不为 NULL,则返回第一个参数的值,否则返回第二个参数的值-- 这里计算有效支付时长(以分钟为单位)coalesce(-- 计算在创建时间和支付时间间隔不超过 30 分钟的情况下,最大支付时间和最小支付时间的差值(以分钟为单位)timestampdiff(minute, min(case when timestampdiff(minute, create_time, pay_time) <= 30 then pay_time end), max(case when timestampdiff(minute, create_time, pay_time) <= 30 then pay_time end)), 0) as valid_pay_duration_min
from orders
-- 按照订单 ID 进行分组
group by order_id;
题目四:交通卡口连续通行分析
-- 使用公共表表达式(CTE),命名为 ordered_passes
with ordered_passes as (-- 从 traffic_log 表中选择数据select car_plate,direction,pass_time,-- 使用 LAG 窗口函数,在每个 car_plate 和 direction 分区内,按照 pass_time 排序-- 获取当前记录的上一条记录的 pass_time,命名为 prev_passlag(pass_time) over (partition by car_plate, direction order by pass_time) as prev_passfrom traffic_log
)
-- 第二个 CTE,命名为 session_flags
, session_flags as (-- 选择 ordered_passes CTE 中的所有列,并添加一个新列 is_new_groupselect *,-- 使用 CASE 语句判断是否为新的连续通行组case -- 如果上一条记录的通过时间为空(即当前是该车牌和方向的第一条记录)-- 或者当前记录的通过时间与上一条记录的通过时间间隔大于 5 分钟when prev_pass is null or timestampdiff(minute, prev_pass, pass_time) > 5 then 1 else 0 end as is_new_groupfrom ordered_passes
)
-- 第三个 CTE,命名为 session_groups
, session_groups as (-- 选择 session_flags CTE 中的所有列,并添加一个新列 group_idselect *,-- 使用 SUM 窗口函数,在每个 car_plate 和 direction 分区内,按照 pass_time 排序-- 对 is_new_group 列进行累加,得到每个连续通行组的唯一标识 group_idsum(is_new_group) over (partition by car_plate, direction order by pass_time) as group_idfrom session_flags
)
-- 第四个 CTE,命名为 aggregated
, aggregated as (-- 选择 car_plate、计算每个连续通行组的最小通过时间、最大通过时间和通过次数select car_plate,min(pass_time) as start_time,max(pass_time) as end_time,count(*) as num_passesfrom session_groups-- 按照 car_plate、direction 和 group_id 进行分组group by car_plate, direction, group_id
)
-- 主查询
select car_plate,start_time,end_time,-- 计算平均速度(单位:km/h),假设每次通过间隔 4 公里((num_passes - 1) * 4)-- 用通过间隔距离除以总时间(以小时为单位),并四舍五入到整数round((num_passes - 1) * 4 / (timestampdiff(minute, start_time, end_time) / 60.0), 0) as avg_speed_kmh
from aggregated
-- 只选择通过次数大于 1 的记录
where num_passes > 1;

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/74119.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PCB钻孔之多边形孔分析

问题分析 在钻孔过程中&#xff0c;钻头的运动可以分为两部分&#xff1a; 公转&#xff1a;钻头的轴线绕理想轴线&#xff08;钻孔中心线&#xff09;做圆周运动。自转&#xff1a;钻头绕自身轴线做旋转运动。 由于公转和自转的叠加&#xff0c;钻尖的运动轨迹会形成复杂的…

Android源码之App启动

目录 App启动概述 App启动过程 App启动过程图 源码概述 跨进程启动 进程内启动 下面以应用桌面Launcher启动App的MainActivity来举例&#xff1a; App启动概述 首先&#xff0c;MainActivity是由Launcher组件来启动的&#xff0c;而Launcher又是通过Activity管理服务Act…

指纹浏览器技术解析:如何实现多账号安全运营与隐私保护

浏览器指纹的挑战与需求 在数字化运营场景中&#xff0c;浏览器指纹技术被广泛用于追踪用户行为。通过采集设备硬件参数&#xff08;如屏幕分辨率、操作系统&#xff09;、软件配置&#xff08;如字体、插件&#xff09;及网络特征&#xff08;如IP地址、时区&#xff09;&…

生活电子常识——cmd不能使用anaconda的python环境,导致输入python打开应用商店

前言 电脑已经安装了anaconda,从自带的Anaconda Prompt (Anaconda3)中是可以识别python环境的&#xff0c;然而切换到cmd时&#xff0c;突然发现cmd中无法识别anaconda的python环境&#xff0c;竟然打开了应用商店让我安装Python&#xff0c;这当然是不对的。 解决 这是因为…

搭建前端环境和后端环境

搭建前端环境 ①、安装vscode&#xff0c;并安装相应的插件工具 ②、安装node.js&#xff0c;可以选择当前版本&#xff0c;或者其他版本 ③、创建工作区 创建一个空文件夹&#xff0c;然后通过vscode工具打开&#xff0c;保存为后缀名为.code-workspace ④、从gitee…

Java基础知识总结(1.8)——Java 注解(持续更新)

更新时间&#xff1a;2025-03-31 Web后端专栏&#xff1a;CSDN专栏——理论-Web后端技术博客总目录&#xff1a;计算机技术系列博客——目录页 8.1 注解的概念 8.1.1 定义与作用 Java注解&#xff08;Annotation&#xff09;是Java语言自JDK1.5版本引入的核心特性&#xff0…

线程概念与控制(下)

线程概念与控制&#xff08;中&#xff09;https://blog.csdn.net/Small_entreprene/article/details/146539064?sharetypeblogdetail&sharerId146539064&sharereferPC&sharesourceSmall_entreprene&sharefrommp_from_link对于之前学习的内容&#xff0c;我们…

SQL注入之盲注技术详解

SQL注入之盲注技术详解 一、盲注基本概念盲注特点&#xff1a; 二、盲注主要类型1. 布尔盲注判断依据&#xff1a; 2. 时间盲注判断依据&#xff1a; 三、布尔盲注详细技术1. 识别布尔盲注2. 数据提取技术(1) 判断数据库类型(2) 获取数据库名长度(3) 逐字符获取数据库名(4) 获取…

OpenCV 图形API(3)高层次设计概览

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 描述 G-API 是一个异构框架&#xff0c;提供了统一的 API 来使用多个支持的后端编程图像处理流水线。 关键的设计理念是在指定使用哪些内核和设备时保持流…

阿里云Tair KVCache:打造以缓存为中心的大模型Token超级工厂

一、Tair KVCache 简介 Tair KVCache 是阿里云瑶池旗下云数据库 Tair 面向大语言模型推理场景推出的 KVCache 缓存加速服务。 随着互联网技术的演进与流量规模的激增&#xff0c;缓存技术逐渐成为系统架构的核心组件。该阶段催生了 Redis 等开源缓存数据库&#xff0c;阿里巴巴…

Open GL ES ->GLSurfaceView正交投影与透视投影方法中近远平面取值参考

坐标系 OpenGL ES使用右手坐标系&#xff0c;相机默认朝向负z方向 相机位置|vz轴<----- 0 -----> -near -----> -far -----不可见 可见区域 不可见裁剪规则 只有z值在[-near, -far]范围内的物体可见&#xff0c; 当z > -near&#xff08;在近平面前&#…

iOS自定义collection view的page size(width/height)分页效果

前言 想必大家工作中或多或少会遇到下图样式的UI需求吧 像这种cell长度不固定&#xff0c;并且还能实现的分页效果UI还是很常见的 实现 我们这里实现主要采用collection view&#xff0c;实现的方式是自定义一个UICollectionViewFlowLayout的子类&#xff0c;在这个类里对…

Java高频面试之并发编程-01

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本baby今天来报道了&#xff01;哈哈哈哈哈嗝&#x1f436; 面试官&#xff1a;并行跟并发有什么区别&#xff1f; 并发 vs 并行&#xff1a;核心区别与场景 1. 定义对比 维度并发&#xff08;Concu…

从零开始学Rust:所有权(Ownership)机制精要

文章目录 第四章&#xff1a;Ownership 所有权核心概念关键机制引用与借用&#xff08;Reference & Borrowing&#xff09;悬垂引用问题错误示例分析解决方案引用安全规则 切片&#xff08;Slice&#xff09;内存安全保证 第四章&#xff1a;Ownership 所有权 Ownership i…

一旦懂得,有趣得紧1:词根tempt-(尝试)的两种解法

词根tempt-尝试 tempt vt.引诱&#xff1b;诱惑&#xff1b;怂恿&#xff1b;利诱&#xff1b;劝诱&#xff1b;鼓动 temptation n.引诱&#xff1b;诱惑 // tempt v.引诱 -ation 名词后缀 attempt v.&n.尝试&#xff0c;试图 // at- 加强 tempt 尝试contempt n.蔑视&am…

召唤数学精灵

1.召唤数学精灵 - 蓝桥云课 问题描述 数学家们发现了两种用于召唤强大的数学精灵的仪式&#xff0c;这两种仪式分别被称为累加法仪式 A(n) 和累乘法仪式 B(n)。 累加法仪式 A(n) 是将从1到 n 的所有数字进行累加求和&#xff0c;即&#xff1a; A(n)12⋯n 累乘法仪式 B(n) …

C语言实现查表8位SAE J1850 CRC

背景&#xff1a; 在做霍尔采集电流的时候&#xff0c;CSSV1500N 系列电流传感器通过can数据输出的报文需要做crc校验&#xff0c;嵌入式常用查表的方式&#xff0c;所以就问了下deepseek怎么算这个CRC. 以下是使用 查表法&#xff08;Lookup Table&#xff09; 在C语言中高效…

【UE5.3.2】初学1:适合初学者的入门路线图和建议

3D人物的动作制作 大神分析:3D人物的动作制作通常可以分为以下几个步骤: 角色绑定(Rigging):将3D人物模型绑定到一个骨骼结构上,使得模型能够进行动画控制。 动画制作(Animation):通过控制骨骼结构,制作出人物的各种动作,例如走路、跳跃、打斗等。 动画编辑(Ani…

mapreduce的工作原理

MapReduce 是 Hadoop 中实现分布式并行计算的核心框架&#xff0c;其工作原理基于“分而治之”的思想&#xff0c;将大规模数据处理任务分解为 Map&#xff08;映射&#xff09; 和 Reduce&#xff08;归约&#xff09; 两个阶段。 一、MapReduce 核心流程 1. Input 阶段 - 输…

换季推广不好做?DeepBI用一键托管的方式,让广告投放跑得快、准、稳

每年换季&#xff0c;尤其是春夏、秋冬交替的节点&#xff0c;都是电商平台上各类季节性商品扎堆上新的高峰期。无论是服饰鞋包、家居户外&#xff0c;还是母婴用品、美妆护肤&#xff0c;许多商品都有着强烈的“时间窗口效应”——一旦错过了热卖期&#xff0c;流量下滑迅速&a…