views:

90

answers:

2

Got a problem with a query I'm trying to write. I have a table that lists people that have been sent an email. There is a bit column named Active which is set to true if they have responded. But I need to count the number of consecutive emails the person has been inactive since either their first email or last active email.

For example, this basic table shows one person has been sent 9 emails. They have been active within two of the emails (3 & 5). So their inactive count would be 4 as we are counting from email number 6 onwards.

PersonID(int)    EmailID(int)    EmailDate(datetime)    Active(bit)
1                1               2009-07-18 19:56:20    0
1                2               2009-08-18 19:56:20    0
1                3               2009-09-18 19:56:20    1
1                4               2009-10-18 19:56:20    0
1                5               2009-11-18 19:56:20    1
1                6               2009-12-18 19:56:20    0
1                7               2010-01-18 19:56:20    0
1                8               2010-02-18 19:56:20    0
1                9               2010-03-18 19:56:20    0

Any pointers or help would be great.

Regards Greg

A: 

give this a try:

SET NOCOUNT ON
DECLARE @Emails table (PersonID int, EmailID int,  Active bit)
INSERT @Emails VALUES ( 1,1,0)
INSERT @Emails VALUES ( 1,2,0)
INSERT @Emails VALUES ( 1,3,1)
INSERT @Emails VALUES ( 1,4,0)
INSERT @Emails VALUES ( 1,5,1)
INSERT @Emails VALUES ( 1,6,0)
INSERT @Emails VALUES ( 1,7,0)
INSERT @Emails VALUES ( 1,8,0)
INSERT @Emails VALUES ( 1,9,0)
INSERT @Emails VALUES ( 2,1,0)
INSERT @Emails VALUES ( 2,2,0)
INSERT @Emails VALUES ( 2,3,0)
INSERT @Emails VALUES ( 2,4,0)
INSERT @Emails VALUES ( 2,5,0)
INSERT @Emails VALUES ( 2,6,0)
INSERT @Emails VALUES ( 3,1,1)
INSERT @Emails VALUES ( 3,2,1)
INSERT @Emails VALUES ( 3,3,1)
INSERT @Emails VALUES ( 3,4,1)
SET NOCOUNT OFF


SELECT
    e.PersonID,COUNT(e.EmailID) AS CountInactive
    FROM @Emails e
        LEFT OUTER JOIN (SELECT
                             PersonID,MAX(EmailID) AS LastActive
                             FROM @Emails
                             WHERE Active=1
                             GROUP BY PersonID
                        ) dt ON e.PersonID=dt.PersonID
    WHERE e.EmailID>ISNULL(dt.LastActive,0)
    GROUP BY e.PersonID

OUTPUT:

PersonID    CountInactive
----------- -------------
1           4
2           6

(2 row(s) affected)

EDIT after OP's edit, same output as above:

SELECT
    e.PersonID,COUNT(e.EmailID) AS CountInactive
    FROM @Emails e
        LEFT OUTER JOIN (SELECT
                             PersonID,MAX(EmailDate) AS LastActive
                             FROM @Emails
                             WHERE Active=1
                             GROUP BY PersonID
                        ) dt ON e.PersonID=dt.PersonID
    WHERE (e.EmailDate>ISNULL(dt.LastActive,0)) OR dt.PersonID IS NULL
    GROUP BY e.PersonID
KM
Hi KM, this also returns good results. Thank you.
Greg
+1  A: 

My first cut:

SELECT PersonID, COUNT(*) FROM Table T1
WHERE Active = 0 AND EmailDate > 
   (SELECT MAX(EMailDate) FROM Table T2 
       WHERE T2.PersonID = T1.PersonID AND Active = 1)
GROUP BY PersonID

Note that this solution requires that each person answers at least one email. If you want to include people who were inactive from the very first email that was sent to them you need to wrap that (MAX(EmailDate)) term inside some kind of IFNULL() returning a date before the start date of the system for NULLs.

Also, as KM points out below, if someone is not currently inactive (they answered the most recent email) they will not be in the result set. I think that probably meets your needs but, if not, let me know.

Larry Lustig
doesn't work if there is more than one employee in the table.
KM
Hi Larry, thanks for this, it seems to be returning good results on my full dataset. Thank you!!
Greg
@KM: Why not? Inner SELECT is correlated to outer PersonID.
Larry Lustig
@Greg: A pleasure to be of assistance.
Larry Lustig
try it on my data, I get 1 row back
KM
What happens if the longest sequence of inactives starts at one?
Thomas
Thanks @KM: I forgot to add to my original post that my solution requires each person to respond to at least one email. In addition, your data exposes another condition, that it won't report people who are not currently inactive (although I think the second case probably matches what the OP wants to see). I will update.
Larry Lustig
@Thomas, that is why my results are differ than this one, my EmployeeID=2 has all rows inactive, my query returns it, but this one doesn't
KM
@KM I don't mean all rows inactive. Using the OP example, suppose rows 1-4 are inactive and that 5 and 7 (and even 9) are active. There are active values but longest sequence is from 1-4.
Thomas
@Larry Lustig, if you run you query against my data you do not find EmployeeID=2 in the result set, but it shoul dhave a count of 6, which has not been active for any e-mails. I would think this would error would not meet the needs since they are looking at counting these rows.
KM
@KM, yes that's true. I edited the answer some time ago to suggest the solution in the event that this is a problem.
Larry Lustig
@Thomas: The OP is not asking for the longest sequence of inactivity, but rather the current sequence of inactivity.
Larry Lustig
@Larry - Ah. You are correct. I missed that in the OP.
Thomas
Just seen all the latest updates to this. Thanks all for the responses. I have got the query up and running in my test system and with a few tweaks (mentioned above) and it's running great. For my first post on this site, I'm very impressed with all the quality and speed in feedback!
Greg