SQL:当无法使用分区列时,如何执行聚合?

SQL:当无法使用分区列时,如何执行聚合?,sql,subquery,case,aggregate,window-functions,Sql,Subquery,Case,Aggregate,Window Functions,以下是该表的图像: 我有一个跟踪用户访问移动应用程序的表格。每行表示用户在应用程序中输入页面的日期时间。Min_btw_页面显示每次页面访问之间的分钟数。当Min_btw_页面>=30分钟时,该会话被视为已完成,下一次页面访问将被视为新会话。我想找到的是: 每个用户HashID每次会话访问的页面数,即行数; 每个会话花费的平均分钟数 我使用了lag函数来创建Min_btw_next_页面。我还创建了列row_no,试图通过HashID按会话为每一行提供序号,但失败了。结果应该与预期的列行号相同。

以下是该表的图像:

我有一个跟踪用户访问移动应用程序的表格。每行表示用户在应用程序中输入页面的日期时间。Min_btw_页面显示每次页面访问之间的分钟数。当Min_btw_页面>=30分钟时,该会话被视为已完成,下一次页面访问将被视为新会话。我想找到的是:

每个用户HashID每次会话访问的页面数,即行数; 每个会话花费的平均分钟数
我使用了lag函数来创建Min_btw_next_页面。我还创建了列row_no,试图通过HashID按会话为每一行提供序号,但失败了。结果应该与预期的列行号相同。但是,即使我能够获得正确的行号,我仍然不知道如何按会话聚合行,因为我无法划分行号。

我对您的问题的理解是,您希望为用户区分“会话”。您将新的“会话”定义为用户在超过30分钟内没有做任何事情的会话。因此,如果有人做了许多动作,每个动作之间有20分钟左右的间隔,它仍然算作一个“会话”

一种方法肯定不是唯一的方法,它是从对现有内容的微小更改开始的。还要注意的是,这里只是一个部分答案——它为以后的分析做准备

还请注意

它是用SQL Server编写的-如果您使用其他内容,则需要查看 如果您以机器可读的形式发布数据,您将获得更快更好的解决方案,因此我们不必重新键入! 我已经避免了分区,除了在第一个延迟中——按照要求。我假设您在LAG中使用了一个分区来获取您的值,所以我在那里使用了一个分区。但是,它确实使用SUM列而不是ORDER BY。。。要得到一个连续的总数。 这里,我要做的是创建一个列,其中“会话”中的所有值都获得相同的值,例如,表中的前六行获得值1,下两行获得值2,下八行获得值3。从那里,你可以分组寻找平均值等,也可以做其他事情,如编号变得微不足道

过程涉及

查找上次访问日期时间,而不是查找下次访问日期时间。这非常重要,因为它使我们能够通过一个简单的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:26'),
('ID1', '20201013 15:28'),
('ID1', '20201013 15:28'),
('ID1', '20201013 15:28'),
('ID1', '20201014 14:59'),
('ID1', '20201014 14:59'),
('ID1', '20201014 16:17'),
('ID1', '20201014 16:46'),
('ID1', '20201014 17:15'),
('ID1', '20201014 17:46');
下面是一个命令,您可以随意将其拆分

我相信,CTE DL_源使用与您拥有的类似的滞后函数来创建原始表,以确定最后的活动时间 CTE DL_会话_源从上面获取数据,并用值1标记新会话 最终选择从DL_会话_源创建运行总数 从这里,您可以随意保存到一个临时表,以便进一步处理,例如

SELECT Session_Num, 
       HashID, 
       COUNT(*) AS Num_Actions, 
       MIN(DeviceDateTime) AS First_Action,  
       MAX(DeviceDateTime) AS Last_Action
FROM #YourTempTable
GROUP BY Session_Num, HashID;

下面是一个添加了一些“交织”数据的示例,例如HashID ID2的无序和重叠,以帮助确保它按要求工作。

我认为实现这些要求的最佳方法是使用DATEDIFF、FIRST_值和整数数学的组合,将分钟差除以30分钟。这将在HashID窗口分区中创建不同的30分钟会话分组。只需要一个CTE

数据类似于seanb

drop table if exists #DeviceLoads;
go
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:26'),
('ID1', '20201013 15:28'),
('ID1', '20201013 15:28'),
('ID1', '20201013 15:28'),
('ID1', '20201014 14:59'),
('ID1', '20201014 14:59'),
('ID1', '20201014 16:17'),
('ID1', '20201014 16:46'),
('ID1', '20201014 17:15'),
('ID1', '20201014 17:46'),
('ID2', '20201014 14:59'),
('ID2', '20201014 16:17'),
('ID2', '20201014 16:27'),
('ID2', '20201014 16:37'),
('ID2', '20201014 16:46'),
('ID3', '20201014 17:15'),
('ID3', '20201014 17:46');
质疑

查询以获取每个HashID的平均会话数(分钟)

with
session_cte as (
    select *,  datediff(minute, first_value(DeviceDatetime) over 
                       (partition by HashID order by DeviceDatetime), 
                        DeviceDatetime)/30 Session_Num
    from #DeviceLoads),
hash_cte as (
    select Session_Num, 
           HashID, 
           count(*) AS Num_Actions, 
           min(DeviceDateTime) AS First_Action,  
           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

请提供示例数据、所需结果和适当的数据库标签。Hi@seanb!谢谢你的提示!我为没有以机器可读的形式发布数据而道歉。您的解决方案非常清晰,非常有用。我真不敢相信我花了一整天的时间试图弄明白这一点,而你这么容易就解决了。你已经用滞后完成了艰苦的工作,我只是把它转过来一点,允许计算一行。但有一件事——在写了这篇文章之后,我在另一个问题中看到了@GMB写的关于的答案,并看到了类似的处理——我认为这也适用于这里。如果您研究“差距和孤岛”,您可能会发现在我编写上述代码时,总体方法相同,但代码更好/更高效,但我没有试图理解该问题/解决方案
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, 
       HashID, 
       count(*) AS Num_Actions, 
       min(DeviceDateTime) AS First_Action,  
       max(DeviceDateTime) AS Last_Action
from session_cte
group by Session_Num, HashID;
with
session_cte as (
    select *,  datediff(minute, first_value(DeviceDatetime) over 
                       (partition by HashID order by DeviceDatetime), 
                        DeviceDatetime)/30 Session_Num
    from #DeviceLoads),
hash_cte as (
    select Session_Num, 
           HashID, 
           count(*) AS Num_Actions, 
           min(DeviceDateTime) AS First_Action,  
           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