In this post I will describe simple mechanism allowing to refer back to previous row in SQL Server 2005/2008 database table.
For the purpose of this article let’s create a table dbo.AgentLog:
create table dbo.AgentLog ( AgentID int ,LogInTime datetime ,LogOutTime datetime ) go insert dbo.AgentLog select 1, '2010-09-08 09:40:00.000', '2010-09-08 10:14:00.000' union all select 1, '2010-09-08 10:35:00.000', '2010-09-08 12:04:00.000' union all select 1, '2010-09-08 13:11:00.000', '2010-09-08 14:24:00.000' union all select 1, '2010-09-08 14:35:00.000', '2010-09-08 16:08:00.000' union all select 2, '2010-09-08 09:00:00.000', '2010-09-08 09:23:00.000' union all select 2, '2010-09-08 10:12:00.000', '2010-09-08 12:05:00.000' union all select 2, '2010-09-08 13:12:00.000', '2010-09-08 15:07:00.000' union all select 2, '2010-09-08 15:10:00.000', '2010-09-08 16:25:00.000' go
Our table will look like this:
AgentID LogInTime LogOutTime ----------- ----------------------- ----------------------- 1 2010-09-08 09:40:00.000 2010-09-08 10:14:00.000 1 2010-09-08 10:35:00.000 2010-09-08 12:04:00.000 1 2010-09-08 13:11:00.000 2010-09-08 14:24:00.000 1 2010-09-08 14:35:00.000 2010-09-08 16:08:00.000 2 2010-09-08 09:00:00.000 2010-09-08 09:23:00.000 2 2010-09-08 10:12:00.000 2010-09-08 12:05:00.000 2 2010-09-08 13:12:00.000 2010-09-08 15:07:00.000 2 2010-09-08 15:10:00.000 2010-09-08 16:25:00.000
The task is to for each agent list all breaks the person made and its length. To do this I need to somehow refer back to previous row to calculate difference in minutes between login and previous logout.
To accomplish the task I will use two features new to SQL Server 2005 (when compared to SQL Server 2000). These are CTE (Common Table Expression) and Row_Number() function.
First one in essence can be thought as variable table of results from SELECT statement. This data set only exists in current scope.
Row_Number() is a SQL Server built-in function that adds extra identity column to data set retrieved with SELECT statement.
SELECT Row_Number() over (partition by AgentID order by LogInTime) AS RowID ,* FROM dbo.AgentLog
Row_Number() takes partition by argument, which tells function that you want unique row numbers against specific column. If this isn’t provided, SQL Server will just number all rows on one-by-one basis. Combining CTE and Row_Number() we can now get the following:
;with AgentLogCTE as
(select Row_Number() over (partition by AgentID order by LogInTime) as RowID, * from dbo.AgentLog )
This CTE then contains following information:
RowID AgentID LogInTime LogOutTime -------------------- ----------- ----------------------- ----------------------- 1 1 2010-09-08 09:40:00.000 2010-09-08 10:14:00.000 2 1 2010-09-08 10:35:00.000 2010-09-08 12:04:00.000 3 1 2010-09-08 13:11:00.000 2010-09-08 14:24:00.000 4 1 2010-09-08 14:35:00.000 2010-09-08 16:08:00.000 1 2 2010-09-08 09:00:00.000 2010-09-08 09:23:00.000 2 2 2010-09-08 10:12:00.000 2010-09-08 12:05:00.000 3 2 2010-09-08 13:12:00.000 2010-09-08 15:07:00.000 4 2 2010-09-08 15:10:00.000 2010-09-08 16:25:00.000
Now what we need to do is just join this table to itself in some clever way, linking current row to previous one:
select t1.AgentID ,t2.LogOutTime as PrevLogout ,t1.LogInTime as Login ,convert(varchar, t1.LogInTime - t2.LogOutTime, 108) as BreakTime from AgentLogCTE t1 join AgentLogCTE t2 on t2.RowID = (t1.RowID-1) and t1.AgentID = t2.AgentID go
And this is it! Resultset looks like the following:
AgentID PrevLogout Login BreakTime ----------- ----------------------- ----------------------- ------------------------------ 1 2010-09-08 10:14:00.000 2010-09-08 10:35:00.000 00:21:00 1 2010-09-08 12:04:00.000 2010-09-08 13:11:00.000 01:07:00 1 2010-09-08 14:24:00.000 2010-09-08 14:35:00.000 00:11:00 2 2010-09-08 09:23:00.000 2010-09-08 10:12:00.000 00:49:00 2 2010-09-08 12:05:00.000 2010-09-08 13:12:00.000 01:07:00 2 2010-09-08 15:07:00.000 2010-09-08 15:10:00.000 00:03:00
Whole SQL script explained in this article can be obtained from here.
Now I have a reason to go for sql2005. This kind of features are amazing and save a lot of efforts Thanks for explaining it in a way so clear and concise
Thanks so much!
Thanks. This was helpful.
Then how do you incorporate fields from more than one table?
Thanks.
Joseph