SQL Server - 每次访问的类似事件之间的持续时间

时间:2014-05-13 13:56:55

标签: sql-server

我正在尝试计算自助服务终端机器上事件之间的时差(以秒为单位)。每当一个人开始使用屏幕时,它就会创建一个VisitID。在该访问中,创建了一系列计算机事件,用于确定屏幕的开始和屏幕的结束我已为每个屏幕确定了每个事件。根据用户的选择,他们可能会或可能不会通过每个事件或屏幕。我的数据如下:

VisitID       ScreenTime           EventName
1              13:24:08         WelcomeScreen_Begin
1              13:24:15         Welcome_End
1              13:24:15         NameScreenInit_Start
1              13:24:32         Name_Screen_Terminate
1              13:24:33         CompanyScreen_Enter
1              13:24:45         Company_Lookup
1              13:24:45         Signoff_Now
1              13:24:52         SignOutEnd
2              13:45:02         WelcomeScreen_Begin
2              13:45:15         Welcome_End
2              13:45:15         NameScreenInit_Start
2              13:45:40         Name_Screen_Terminate
2              13:45:40         Signoff_Now
2              13:45:58         SignOutEnd

我希望我的结果看起来像这样:

VisitID       WelcomeScreen       NameScreen      CompanyScreen    SignoffScreen
1               00:00:7           00:00:17           00:00:12          00:00:07
2               00:00:13          00:00:25              NULL           00:00:18

最终,我想计算在每个屏幕上花费的平均时间:

WelcomeScreenAVG      NameScreenAVG      CompanyScreenAVG      SignoffScreenAVG
00:00:10              00:00:21           00:00:12              00:00:13

我已经能够使用下标来获取DATEDIFF子句中的每个时间戳,但只有在WHERE子句中包含VisitID时它才有效。我有成千上万的条目,所以不是这样做的。我好久以来一直绞尽脑汁。有没有人有解决方案,或者至少知道我应该做什么呢?提前谢谢!

当可用于表示一个屏幕的开头的唯一事件是结束先前屏幕的同一事件时,会发生一些事件。例如:

VisitID       ScreenTime       EventName
34            08:34:36         Delivery_summaryConfirm
34            08:34:47         Seal_questionsBegin
34            08:35:17         EndSeal_Inquiry

^^这是2个屏幕^^在上面的示例中,虽然Seal_questionsBegin是SealInquiry屏幕的开头,但它是唯一能够表示DeliverySummary屏幕结束的事件。这是因为DeliverySummary屏幕后面总是跟着SealInquiry屏幕。

3 个答案:

答案 0 :(得分:0)

将数据集拆分为"开始"和"结束",然后通过以下方式进行简单的连接和组:

SELECT 
    Begins.ScreenName,
    AVG(DateDiff(second, Begins.ScreenTime, Ends.ScreenTime)) AS TimeSpentSeconds
FROM (
    SELECT 
        VisitID, 
        LEFT(EventName, LEN(EventName)-5) AS ScreenName, 
        ScreenTime 
    FROM MyTable WHERE EventName LIKE '%Begin'
) AS Begins
INNER JOIN (
    SELECT 
        VisitID, 
        LEFT(EventName, LEN(EventName)-3) AS ScreenName, 
        ScreenTime 
    FROM MyTable WHERE EventName LIKE '%End'
) AS Ends
ON Begins.VisitID = Ends.VisitID AND Begins.ScreenName = Ends.ScreenName
GROUP BY Begins.ScreenName

以行方式返回数据:

ScreenName      TimeSpentSeconds
----------      ----------------
WelcomeScreen   10
NameScreen      21
CompanyScreen   12
SignoffScreen   13

使用PIVOT将其更改为逐列布局。要将秒数更改为小时 - 分钟 - 秒,请参阅示例this post

答案 1 :(得分:0)

更新

如果您有每个屏幕的固定事件列表,您可以执行以下操作。我认为它看起来更容易阅读,并且比其他帖子更简单......但那就是我。

New Sql Fiddle

With tblUserTimes as (
  Select VisitId, 

  DateDiff(second, 
  Max(Case When EventName = 'WelcomeScreen_Begin' Then ScreenTime End),
  Max(Case When EventName = 'Welcome_End' Then ScreenTime End)) as WelcomeScreen,

  DateDiff(second, 
  Max(Case When EventName = 'NameScreenInit_Start' Then ScreenTime End),
  Max(Case When EventName = 'Name_Screen_Terminate' Then ScreenTime End)) as NameScreen,

  DateDiff(second, 
  Max(Case When EventName = 'CompanyScreen_Enter' Then ScreenTime End),
  Max(Case When EventName = 'Company_Lookup' Then ScreenTime End)) as CompanyScreen,

  DateDiff(second, 
  Max(Case When EventName = 'Signoff_Now' Then ScreenTime End),
  Max(Case When EventName = 'SignOutEnd' Then ScreenTime End)) as SignoffScreen

  from Visits
  Group By VisitId)

Select AVG(WelcomeScreen) as WelcomeScreenAvg,
       AVG(NameScreen) as NameScreenAvg,
       AVG(CompanyScreen) as CompanyScreenAvg,
       AVG(SignoffScreen) as SignoffScreenAvg
from tblUserTimes

请参阅this SQL Fiddle,例如使用数据透视。

答案 2 :(得分:0)

首先想到你需要的是规范化事件名称,查询要注意这一点,你是否可以规范化数据或者至少在你的数据库中创建一个翻译表

修改
这些事件比我最初的想法更多,翻译应该被创建为一个完整的表格。

我们可以利用Translate表,添加IsBegin和IsEnd位字段并将NormName字段映射到屏幕名称,除了字典外,还会生成Times {{1}中的简化逻辑}}

CTE

如果事件名称既是事件的结尾又是新事件的开始,则应该插入两次,如' Seal_questionsBegin'在示例中

CREATE TABLE NormalizeEvents (
  RealName VARCHAR(50)
, NormName VARCHAR(50)
, IsBegin BIT
, IsEnd BIT
)

INSERT INTO NormalizeEvents (RealName, NormName, IsBegin, IsEnd)
VALUES ('WelcomeScreen_Begin', 'WelcomeScreen', 1, 0)
     , ('Welcome_End', 'WelcomeScreen', 0, 1)
     , ('NameScreenInit_Start', 'NameScreen', 1, 0)
     , ('Name_Screen_Terminate', 'NameScreen', 0, 1)
     , ('CompanyScreen_Enter', 'CompanyScreen', 1, 0)
     , ('Company_Lookup', 'CompanyScreen', 0, 1)
     , ('Signoff_Now', 'SignoffScreen', 1, 0)
     , ('SignOutEnd', 'SignoffScreen', 0, 1)
     , ('Delivery_summaryConfirm', 'DeliverySummary', 1, 0)
     , ('Seal_questionsBegin', 'DeliverySummary', 0, 1)
     , ('Seal_questionsBegin', 'SealQuestions', 1, 0)
     , ('EndSeal_Inquiry', 'SealQuestions', 0, 1)

SQLFiddle demo

要获取每个屏幕的全局平均值,只需删除WITH Times AS ( SELECT VisitID, EventName = t.NormName , ScreenTimeBegin = Max(CASE t.IsBegin WHEN 1 THEN ScreenTime ELSE NULL END) , ScreenTimeEnd = Max(CASE t.IsEnd WHEN 1 THEN ScreenTime ELSE NULL END) FROM Events e INNER JOIN NormalizeEvents t ON e.EventName = t.RealName GROUP BY VisitID, NormName ) SELECT VisitID , [WelcomeScreen] = CAST(DATEADD(ss, WelcomeScreen, '1900-01-01') as Time) , [CompanyScreen] = CAST(DATEADD(ss, CompanyScreen, '1900-01-01') as Time) , [NameScreen] = CAST(DATEADD(ss, NameScreen, '1900-01-01') as Time) , [SignoffScreen] = CAST(DATEADD(ss, SignoffScreen, '1900-01-01') as Time) , [DeliverySummary]=CAST(DATEADD(ss, DeliverySummary, '1900-01-01') as Time) , [SealQuestions] = CAST(DATEADD(ss, SealQuestions, '1900-01-01') as Time) FROM (SELECT VisitID, EventName , Duration = DateDiff(ss, ScreenTimeBegin, ScreenTimeEnd) FROM Times) SOURCE PIVOT (AVG(Duration) FOR EventName IN ([WelcomeScreen], [CompanyScreen], [NameScreen], [SignoffScreen] , [DeliverySummary], [SealQuestions]) ) pvt 的来源中的VisitID字段和主查询。

PIVOT

SQLFiddle demo(数据过时)。

由于我假设SQLServer 2012或更高版本的问题未指定SQLServer版本。