如何解决SQL:无法使用PARTITION列时如何执行聚合?
下面是表格的图像:
https://i.stack.imgur.com/mPUGV.png
我有一个表,用于跟踪用户对移动应用程序的访问。每行代表用户在应用程序中进入页面的日期时间。 Min_btw_page显示每次访问页面之间的分钟数。如果Min_btw_page> = 30分钟,则该会话被视为完成,下一次页面访问将被计为新会话。我想找到的是:
- 每个用户每个会话访问的页面数(即行数)(HashID);
- 每个会话平均花费的时间
我已经使用lag()函数来创建“ Min_btw_next_page”。我还创建了“ row_no”列,尝试给出一个序列号。通过会话的HashID到每一行,但是我失败了。结果应类似于“ Expected_row_no”列。但是,即使我能够获得正确的行号,我仍然不知道如何按会话聚合行,因为我无法对行号进行分区。
解决方法
我对您的问题的理解是,您希望区分用户的“会话”。您将新的“会话”定义为用户超过30分钟未执行任何操作的会话。因此,如果某人做了很多动作,每次动作之间相隔20分钟左右,它仍然算作一个“会话”。
一种方法(绝对不是唯一的方法)将首先对您所拥有的内容进行微小的更改。还请注意,这只是部分答案-为以后的分析做准备。
请注意
- 它是用SQL Server写的-如果您使用其他内容,则需要进行审核
- 如果以机器可读形式发布数据,您将获得更快,更好的解决方案,因此我们无需重新输入!
- 我已避免按要求进行分区(第一个LAG中除外)。我假设您在LAG中使用了一个分区来获取您的值,所以我在那里使用了一个分区。但是,它确实使用
SUM(column) OVER (ORDER BY ...)
来获得运行总计。
在这里,我正在做的是创建一列,其中“会话”中的所有值都获得相同的值,例如,表中的前六行获得值1,接下来的两行获得值2,下一行八行获得值3。从那里,您可以分组以查找平均值等,并且进行编号之类的其他事情也变得微不足道。
过程涉及
- 找到最后访问日期时间,而不是找到 next VisitDateTime。这非常重要,因为它使我们能够通过简单的DATEDIFF(在一行中)确定是否是新会话
- 作为“新会话”的每一行都标记为值1,否则为0。
- 然后通过简单地获取这些标记的运行总数来创建会话号
数据设置
CREATE TABLE #DeviceLoads (LogID int IDENTITY(1,1),HashID nvarchar(10),DeviceDatetime datetime);
INSERT INTO #DeviceLoads (HashID,DeviceDatetime) VALUES
('ID1','20201013 15:26'),('ID1','20201013 15:28'),'20201014 14:59'),'20201014 16:17'),'20201014 16:46'),'20201014 17:15'),'20201014 17:46');
这是一个命令(不过请随意拆分)。
- CTE
DL_Source
使用LAG函数(类似于您所拥有的功能来创建原始表)来确定上次活动时间 - CTE
DL_Session_Source
从上方获取数据,并用值1标记新的会话。 - 最终的SELECT从
DL_Session_Source
创建运行总计
WITH DL_source AS -- This is probably similar to what you have already
(SELECT LogID,HashID,DeviceDatetime,LAG(DeviceDatetime,1) OVER (PARTITION BY HashId ORDER BY DeviceDatetime,LogID) AS Last_DeviceDateTime
FROM #DeviceLoads),DL_Session_Source AS
(SELECT LogID,Last_DeviceDateTime,CASE WHEN DATEDIFF(minute,DeviceDatetime) <= 30 THEN 0 ELSE 1 END AS New_Session_flag
FROM DL_source)
SELECT *,SUM(New_Session_flag) OVER (ORDER BY HashID,LogID) AS Session_Num
FROM DL_Session_Source;
这是结果(为简洁起见,将秒数缩短)。请注意末尾的列(Session_Num),该列指示哪个会话处于哪个会话中。
LogID HashID DeviceDatetime Last_DeviceDateTime New_Session_flag Session_Num
1 ID1 2020-10-13 15:26 NULL 1 1
2 ID1 2020-10-13 15:26 2020-10-13 15:26 0 1
3 ID1 2020-10-13 15:28 2020-10-13 15:26 0 1
4 ID1 2020-10-13 15:28 2020-10-13 15:28 0 1
5 ID1 2020-10-13 15:28 2020-10-13 15:28 0 1
6 ID1 2020-10-14 14:59 2020-10-13 15:28 1 2
7 ID1 2020-10-14 14:59 2020-10-14 14:59 0 2
8 ID1 2020-10-14 16:17 2020-10-14 14:59 1 3
9 ID1 2020-10-14 16:46 2020-10-14 16:17 0 3
10 ID1 2020-10-14 17:15 2020-10-14 16:46 0 3
11 ID1 2020-10-14 17:46 2020-10-14 17:15 1 4
在这里,随时保存到临时表中以进行进一步处理,例如
SELECT Session_Num,COUNT(*) AS Num_Actions,MIN(DeviceDateTime) AS First_Action,MAX(DeviceDateTime) AS Last_Action
FROM #YourTempTable
GROUP BY Session_Num,HashID;
这里是db<>fiddle,其中添加了一些“交织的”数据(例如,HashID ID2的乱序和重叠),以确保数据按要求工作。
,我认为达到要求的最佳方法是结合使用DATEDIFF
,FIRST_VALUE
和整数数学将分钟差除以30分钟。这会在HashID窗口分区内创建不同的30分钟会话分组。只需一个CTE。
数据(类似于seanb)
drop table if exists #DeviceLoads;
go
create table #DeviceLoads (
LogID int identity(1,HashID nvarchar(10),DeviceDatetime datetime);
insert into #DeviceLoads (HashID,DeviceDatetime) values
('ID1','20201014 17:46'),('ID2','20201014 16:27'),'20201014 16:37'),('ID3','20201014 17:46');
查询
with session_cte as (
select *,datediff(minute,first_value(DeviceDatetime) over
(partition by HashID order by DeviceDatetime),DeviceDatetime)/30 Session_Num
from #DeviceLoads)
select Session_Num,count(*) AS Num_Actions,min(DeviceDateTime) AS First_Action,max(DeviceDateTime) AS Last_Action
from session_cte
group by Session_Num,HashID;
查询以分钟为单位获取每个HashID的平均会话次数
with
session_cte as (
select *,DeviceDatetime)/30 Session_Num
from #DeviceLoads),hash_cte as (
select Session_Num,max(DeviceDateTime) AS Last_Action
from session_cte
group by Session_Num,HashID)
select HashID,avg(datediff(minute,First_Action,Last_Action)*1.0) avg_session_min
from hash_cte
group by HashID;
输出
HashID avg_session_min
ID1 0.333333
ID2 6.333333
ID3 0.000000
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。