Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2008 Forums
 Transact-SQL (2008)
 Count Consecutive

Author  Topic 

Noz2k
Starting Member

3 Posts

Posted - 2011-12-21 : 06:07:07
Hi, What I am trying to achieve is to look through all rows in a single table, and return a count for each person in the table (each person can exist in the table multiple times) where the attendance column = 'absent' consecutively. For instance looking at 1 person's record it might be something like this.

DAYID PERSON ATTENDANCE
1 NOZ2k Absent
2 NOZ2k Absent
5 NOZ2k Attended
6 NOZ2k Absent
10 NOZ2k Attended
11 NOZ2k Absent
12 NOZ2k Absent
18 NOZ2k Absent


so I would want 3 values returned for NOZ2k, like this

PERSON CONSECUTIVE_ABSENCE
NOZ2k 2
NOZ2k 1
NOZ2k 3



To give you a better id of the table structure here is a query I have which calculates the percentage of absence for each person

SELECT Person, Absent, Attended, Absent/(Absent + Attended) As PercentageAbsent
FROM
(
SELECT Person, SUM(CASE WHEN Attendance IN 'ABSENT'
THEN 1
ELSE 0
END) As Absent,
SUM(CASE WHEN Attendance not in 'ABSENT'
THEN 1
ELSE 0
END) As Attended
FROM db.attendance
WHERE day_id BETWEEN 3486 AND 3669
GROUP BY Person
)
WHERE Absent >= 5
GROUP BY Person, Absent, Attended
ORDER BY PercentageAbsent DESC;



Thanks in advance for any help

sunitabeck
Master Smack Fu Yak Hacker

5155 Posts

Posted - 2011-12-21 : 07:19:44
If you google for "Islands and Gaps in SQL", you will find examples of this type of problem. There are a few different ways to do it - one of the more efficient ones is as follows:

---------------------------------------------------------------------------
CREATE TABLE #tmp(dayid INT, person VARCHAR(32), attendance VARCHAR(32));

INSERT INTO #tmp VALUES
(1,'NOZ2k','Absent'),
(2,'NOZ2k','Absent'),
(5,'NOZ2k','Attended'),
(6,'NOZ2k','Absent'),
(10,'NOZ2k','Attended'),
(11,'NOZ2k','Absent'),
(12,'NOZ2k','Absent'),
(18,'NOZ2k','Absent');


---------------------------------------------------------------------------
;WITH cte1 AS
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY person ORDER BY dayId) AS RN
FROM
#tmp
),
cte2 AS
(
SELECT
*,
RN - ROW_NUMBER() OVER(PARTITION BY person,Attendance ORDER BY dayId) AS grp
FROM
cte1
)
SELECT
person,
COUNT(*) AS Consecutive_Absence
FROM
cte2
WHERE
Attendance = 'Absent'
GROUP BY
person,
grp;

---------------------------------------------------------------------------
DROP TABLE #tmp;
---------------------------------------------------------------------------
Go to Top of Page

Noz2k
Starting Member

3 Posts

Posted - 2011-12-21 : 07:58:32
Thanks sunitabeck,

I will have a look into Islands & Gaps, and see if I can understand how to produce a solution.

However there are a couple of issues in using your suggested, firstly I was just using the NOZ2k as an example, I need to return these results for upwards of 20,000 distinct Person entries.
and secondly I unfortunately do not have the required permissions to create tables in the database.
Go to Top of Page

sunitabeck
Master Smack Fu Yak Hacker

5155 Posts

Posted - 2011-12-21 : 13:02:19
The code that I posted does not require you to create any tables. The temporary table that I created was only for demonstrating what the code would do. The data I inserted which has person = NOZ2k is also for demonstration only.

The query is designed to work for more than one user. The section that has the "PARTITION BY person" is meant for that purpose.
Go to Top of Page
   

- Advertisement -