SQL时差总计

时间:2018-03-01 19:35:51

标签: sql sql-server

+---------+-----------+----------+---------+
| FileID  | EventDate | UserName | EventID |
+---------+-----------+----------+---------+
| 1000001 | DateTime  | User1    |       1 |
| 1000001 | DateTime  | User1    |       3 |
| 1000001 | DateTime  | User1    |       3 |
| 1000001 | DateTime  | User1    |       1 |
| 1000001 | DateTime  | User1    |       3 |
| 1000001 | DateTime  | User1    |       3 |
| 1000001 | DateTime  | User1    |       3 |
| 1000002 | DateTime  | User1    |       1 |
| 1000002 | DateTime  | User1    |       3 |
+---------+-----------+----------+---------+

我有一个表格,其中的事件数据与上面的例子类似。每个事件都有一个FileID,EventID,UserName和一个Datetime。事件ID 1是打开文件的用户,事件ID 3是保存事件。事件ID 3的最后一行表示文件关闭,因为每个文件都在关闭时保存。我需要能够将用户在文件中的总时间(最终事件3的时间 - 事件1的时间)相加,但如果用户多次输入文件,则无法确定如何执行此操作。有任何想法吗?

3 个答案:

答案 0 :(得分:1)

create table events (FileID int, EventDate datetime, UserName varchar(10), EventID int);
insert into events values
(1000001, '20180101 10:00:00', 'User1', 1 ),
(1000001, '20180101 10:30:00', 'User1', 3 ),
(1000001, '20180101 10:45:00', 'User1', 3 ),
(1000001, '20180102 11:00:00', 'User1', 1 ),
(1000001, '20180102 11:30:00', 'User1', 3 ),
(1000001, '20180102 11:45:00', 'User1', 3 ),
(1000001, '20180102 12:00:00', 'User1', 3 ),
(1000002, '20180101 05:00:00', 'User1', 1 ),
(1000002, '20180101 05:45:00', 'User1', 3 );

GO
9 rows affected
select FileID, sum(minutes) totalTime
from (
      select FileID, grp, datediff(minute, min(EventDate), max(EventDate)) minutes
      from (
            select FileID, EventDate, UserName, EventID,
                   sum(iif(EventID = 1,1,0)) over (order by FileID, EventDate) grp
            from   events
           ) t
      group by FileId, grp
     ) t2
group by FileID
GO
 FileID | totalTime
------: | --------:
1000001 |       105
1000002 |        45

dbfiddle here

答案 1 :(得分:0)

只是做一些奇特的连接:

select fileID,userName,eventDate 'startDate',min(t2.eventDate) 'nextStart'
into #temp1
from logTable t1
left join logTable t2 on t1.fileID=t2.fileID and t1.userName=t2.userName and t2.eventID=1 and t1.eventDate<t2.eventDate
where t1.eventID=1
group by fileID,eventDate,userName

select fileID,userName,startDate,max(t2.eventDate) 'endDate'
from #temp1 t1
left join logTable t2 on t1.fileID=t2.fileID and t1.userName=t2.userName and t2.eventID=3 and t2.eventDate>t1.startDate and t2.eventDate<t1.nextStart 

您无法获得所花费的时间,直到用户再次打开相同的文件,因为您没有最近的时间。但您可以将第二个连接修改为...(t2.eventDate<t1.nextStart or t1.nextStart is null)这将获得到最后一个保存点的开始时间

答案 2 :(得分:0)

再次使用LEAD()LAG(),这是SQL2012 +。

SQL Fiddle

MS SQL Server 2017架构设置

CREATE TABLE t ( FileID int, EventDate datetime, UserName varchar(20), EventID int ) ;
INSERT INTO t (FileID, EventDate, UserName, EventID)
VALUES 
    (1000001,'2018-01-01T00:00:00.000','User1',1) /* OPEN 1 */
  , (1000001,'2018-01-01T00:01:15.000','User1',3) /* SAVE 1 */
  , (1000001,'2018-01-01T00:01:30.000','User1',3) /* CLOSE 1 */ /* 1 = 0:01:30 */
  , (1000001,'2018-01-01T00:02:00.000','User1',1) /* OPEN 2 */
  , (1000001,'2018-01-01T00:02:15.000','User1',3) /* SAVE 2 */
  , (1000001,'2018-01-01T00:02:30.000','User1',3) /* SAVE 2 */
  , (1000001,'2018-01-01T00:03:00.000','User1',3) /* SAVE 2 */
  , (1000002,'2018-02-01T00:00:00.500','User1',1) /* OPEN 1 */
  , (1000001,'2018-01-01T00:10:00.000','User1',3) /* CLOSE 2 */ /* 2 = 0:08:00 */
  , (1000002,'2018-02-01T00:01:00.000','User1',3) /* SAVE 1 */
  , (1000002,'2018-02-01T00:02:00.000','User1',3) /* CLOSE 1 */ /* 1 = 0:01:59.500 */
  , (1000088,'2018-02-10T00:00:00.000','NoCloseUser',1) /* OPEN - NOT CLOSED */
  , (1000099,'2018-02-10T00:00:00.000','NoOpenUser',3) /* CLOSED - NOT OPENED */
;

我添加了几个糟糕的记录。一个没有关闭日期,但仍会显示在结果中。这纯粹是坏事,没有记录开放日期,但不会弄乱数据。可以在查询中过滤Opened-But-Not-Closed记录。

<强>查询

; WITH closes AS (
  SELECT * 
  FROM (
    SELECT t.FileID, t.UserName, t.EventDate, t.EventID
      , LEAD(t.EventID) OVER (PARTITION BY t.FileID, t.UserName ORDER BY t.EventDate) AS leadEvent 
    FROM t
  ) s1
  WHERE s1.EventID = 3 AND COALESCE(s1.leadEvent,1) = 1
)
, opens AS (
  SELECT t.FileID, t.UserName, t.EventDate
    , LEAD(t.EventDate) OVER ( PARTITION BY t.FileID, t.UserName ORDER BY t.EventDate ) AS nextOpen  
  FROM t 
  WHERE t.EventID = 1
)
SELECT s2.FileID, s2.UserName, SUM(minOpen) AS totalMinOpen 
FROM (
  SELECT o.FileID, o.UserName
    , o.EventDate as openDate
    , o.nextOpen
    , c.EventDate closeDate
    , c.leadEvent
    , DATEDIFF(second, o.EventDate,COALESCE(c.EventDate,getDATE()))/60.0 AS minOpen
  FROM opens o
  LEFT OUTER JOIN closes c ON o.FileID = c.FileID
    AND o.UserName = c.UserName
    AND o.EventDate <= COALESCE(c.EventDate,getDATE()) 
    AND ( c.leadEvent IS NOT NULL OR o.nextOpen IS NULL )
) s2
GROUP BY s2.FileID, s2.UserName

<强> Results

|  FileID |    UserName | totalMinOpen |
|---------|-------------|--------------|
| 1000001 |       User1 |          9.5 |
| 1000002 |       User1 |            2 |
| 1000088 | NoCloseUser |        28694 |

我使用LEAD()LAG()来确定文件关闭位置然后再次打开之间的区域。我使用CTE来存储用于确定打开和关闭的各个查询。我将它差异化为秒,然后除以60.0得到分钟的十进制表示。我确信这个查询可以稍微优化一下。