There's probably a more elegant solution but using your criteria from your comment to @Erik that the logout will be recorded this should get you what you need, or very close:
DECLARE @UserID sysname;
DECLARE @LastUserID sysname;
DECLARE @Time datetime;
DECLARE @EventType varchar(10);
DECLARE @CurrentSessionRowID int;
CREATE TABLE #results (
ID int IDENTITY
PRIMARY KEY
, UserID sysname
, LoginTime datetime
, LogoutTime datetime
, [Duration(Hours)] AS DATEDIFF(mi, LoginTime, LogoutTime) / 60. -- datediff by minutes so partial hours can be displayed in the final results
);
DECLARE userSessions CURSOR FAST_FORWARD
FOR
-- find all the user sessions in the order they were input for each user
SELECT t.UserID
, t.[Time]
, t.EventType
FROM dbo.test AS t
ORDER BY t.UserID
, t.ID;
OPEN userSessions;
FETCH NEXT FROM userSessions INTO @UserID, @Time, @EventType;
SET @LastUserID = @UserID;
WHILE @@FETCH_STATUS = 0
BEGIN
-- same user, determine if this is a login or logout and record it in the #results table
IF @UserID = @LastUserID
BEGIN
IF @EventType = 'login'
BEGIN
INSERT INTO #results
(UserID, LoginTime)
VALUES (@UserID, @Time);
SET @CurrentSessionRowID = SCOPE_IDENTITY();
END;
IF @EventType = 'logout'
BEGIN
UPDATE #results
SET LogoutTime = @Time
WHERE ID = @CurrentSessionRowID;
END;
END;
ELSE
BEGIN
-- new user, make sure the first record is login and start recording sessions
IF @EventType = 'login'
BEGIN
INSERT INTO #results
(UserID, LoginTime)
VALUES (@UserID, @Time);
SET @CurrentSessionRowID = SCOPE_IDENTITY();
END;
END;
SET @LastUserID = @UserID;
FETCH NEXT FROM userSessions INTO @UserID, @Time, @EventType;
END;
DEALLOCATE userSessions;
-- Query the results however you want with grouping and aggregates
SELECT *
FROM #results AS r;
DROP TABLE #results;
DATETIME
andDATETIME2
are compatible data types in the application layer so you should be able to upgrade toDATETIME2
. Plus you should be able to wrap data access into a stored proc which would let you accept a string and store an int forEventType
. Also you should consider not using a data type keyword as your column name. The point of the edge cases I mentioned was to get you thinking about ways your script can fail. Under your current system if I get the blue screen of death at the end of my work day and leave without restarting the application it would show me as active all night