2

I have the following table:

ID (int)
EMAIL (varchar(50))
CAMPAIGNID (int)
isSubscribe (bit)
isActionByUser (bit)

This table stores all subscribe and unsubscribe actions on campaigns for a user. These actions can be done by the user itself (isActionByUser = true) or by the administration desk (isActionByUser = false).

I need to get the last action to determine if a user is subscribed or unsubscribed. But keeping in mind that when a user did an unsubscribe action from a campaign, it will have priority on other subscribe actions by the administration desk.

I have found a nice solution to get the lastest record grouped by EMAIL and CAMPAIGNID. But I can't figure out how I do incorporate the requirement that an isActionByUser = true, has absolute priority over records with isActionByUser = false. Also: when the administration desk does an unsubscribe action, it will have priority over a record with (isSubscribe = true and isActionByUser).

Example data:

ID    EMAIL    CAMPAIGNID    ISSUBSCRIBE    ISACTIONBYUSER
-----------------------------------------------------------
1     a@aa.com    1             1                0
2     b@bb.com    1             1                0
3     c@cc.com    1             1                0
4     a@aa.com    1             0                1
5     a@aa.com    1             1                0
6     c@cc.com    1             1                1
7     c@cc.com    1             0                0

The expected result would be:

ID    EMAIL    CAMPAIGNID    ISSUBSCRIBE    ISACTIONBYUSER
-----------------------------------------------------------
2     b@bb.com    1             1                0   
4     a@aa.com    1             0                1
7     c@cc.com    1             0                0

With the following query

select cs1.*
from 
    [TABLE] cs1 
    left join 
    [TABLE] cs2
    on 
    cs1.EM_EMAIL = cs2.EM_EMAIL
    and 
    cs1.EM_CAMPAIGNID = cs2.EM_CAMPAIGNID
    and 
    cs1.id < cs2.id
where cs2.id is null

I' m having the following result:

ID    EMAIL    CAMPAIGNID    ISSUBSCRIBE    ISACTIONBYUSER
-----------------------------------------------------------
2     b@bb.com    1             1                0
5     a@aa.com    1             1                0
7     c@cc.com    1             0                0

Another approach:

SELECT *
FROM [TABLE] cs
WHERE id in 
  (
    SELECT top 1 id 
    FROM [TABLE] ss
    WHERE 
        cs.EMAIL = ss.EMAIL
        and 
        cs.CAMPAIGNID = ss.CAMPAIGNID 
        and ISSUBSCRIBE = (
            select top 1 min(convert(int, ISSUBSCRIBE)) 
            FROM [TABLE] sss
            WHERE 
                cs.EMAIL = sss.EMAIL
                and 
                cs.CAMPAIGNID = sss.CAMPAIGNID
            )
       and ISACTIONBYUSER= (
            select top 1 max(convert(int, ISACTIONBYUSER)) 
            FROM [TABLE] ssss
            WHERE 
                cs.EMAIL = ssss.EMAIL
                and 
                cs.CAMPAIGNID = ssss.CAMPAIGNID
            )
        )   

This will produce the following result:

ID    EMAIL    CAMPAIGNID    ISSUBSCRIBE    ISACTIONBYUSER
-----------------------------------------------------------
2     b@bb.com    1             1                0
4     a@aa.com    1             0                1
6     c@cc.com    1             1                1

Which is also not correct. And I'm afraid performance will be a big rpoblem with this approach.

So any ideas how I can solve this?

Community
  • 1
  • 1
CyclingFreak
  • 1,614
  • 2
  • 21
  • 40
  • Do you need the ID of the row where the data comes from? – Tim Lehner Mar 27 '12 at 15:40
  • Also, with "when the administration desk does an unsubscribe action, it will have priority over a record with (isSubscribe = true and isActionByUser)", do you mean that if the admin desk unsubscribes a user, that user cannot subscribe? – Tim Lehner Mar 27 '12 at 17:59
  • @TimLehner I need all the data. Plan to make a view of it which I can reference in .NET Entity Framework. – CyclingFreak Mar 28 '12 at 06:46
  • @TimLehner If the administration desk unsubscribes a user, and then the user subscribes again to the same campaign, the action made by the user has the priority over the action by the administration desk. The result is thus that the user is subscribed again. – CyclingFreak Mar 28 '12 at 06:48

3 Answers3

3

Ok, try the following query:

SELECT DISTINCT B.*
FROM YourTable A
OUTER APPLY (SELECT TOP 1 *
             FROM YourTable
             WHERE Email = A.Email AND CampaignId = A.CampaignId
             ORDER BY CASE WHEN ISSUBSCRIBE = 0 THEN 1 ELSE 2 END,
             CASE WHEN ISACTIONBYUSER = 1 THEN 1 ELSE 2 END,
             ID DESC) B
Lamak
  • 69,480
  • 12
  • 108
  • 116
  • +1 but this part `CASE WHEN ISSUBSCRIBE = 0 THEN 1 ELSE 2 END` should probably be `CASE WHEN ISSUBSCRIBE = 0 AND ISACTIONBYUSER = 0 THEN 1 ELSE 2 END`. This `ISSUBSCRIBE = 0` only takes priority when its done by an admin. The user should be able to flip it back and forth *until* the admin does it. – Conrad Frix Mar 27 '12 at 15:36
  • @ConradFrix - Well, I wasn't entirely clear about that part. On the question it says: "an unsubscribe action, it will have priority over a record with (isSubscribe = true and isActionByUser)" so it seems that an unsubscribe has the actual top priority. The fact that ID 7 should be the one selected over ID 6 seems to confirm that – Lamak Mar 27 '12 at 15:39
  • This seems to return the same results of the OP's first failed attempt shown. – Tim Lehner Mar 27 '12 at 15:44
  • 1
    @TimLehner - You can see a working example on this link http://sqlfiddle.com/#!3/8192b/7 , it returns the desired output – Lamak Mar 27 '12 at 15:46
  • I must be missing something. The OP wants IDs 2,4,7. This returns 2,5,7. – Tim Lehner Mar 27 '12 at 15:49
  • @TimLehner - No, you are right, I confused the desired output. It is working fine now, thanks – Lamak Mar 27 '12 at 15:55
  • Got it. This also does not account for a user unsubscribing then subscribing again later. Not sure how the OP feels about that. – Tim Lehner Mar 27 '12 at 15:59
  • @Lamak it looks like you cut the beginning of the quote. "Also: **when the administration desk does an unsubscribe** action, it will have priority over a record with (isSubscribe = true and isActionByUser)." As written you have it **any unsubscribe action will have priority** – Conrad Frix Mar 27 '12 at 16:25
  • @Lamak I really appreciate your help. I have created some more complex scenario's to test. But you query does not return the correct results in 4 of 11 scenario's. If you are interested in this, I can provide you those scenario's. – CyclingFreak Mar 28 '12 at 07:57
2

Try this: [Updated to handle unsubscribe and subscribed users]

    declare @test table (id int, email varchar(100), CAMPAIGNID int, ISSUBSCRIBE bit, ISACTIONBYUSER bit)
INSERT INTO @test 
SELECT 1,'a@aa.com',1,1,0 UNION 
SELECT 2,'b@bb.com',1,1,0 UNION 
SELECT 3,'c@cc.com',1,1,0 UNION 
SELECT 4,'a@aa.com',1,0,1 UNION 
SELECT 5,'a@aa.com',1,1,0 UNION 
SELECT 6,'c@cc.com',1,1,1 UNION 
SELECT 7,'c@cc.com',1,0,0 UNION
select 8, 'd@dd.com', 1, 1, 1 UNION 
select 9, 'd@dd.com', 1, 0, 1 UNION 
select 10, 'd@dd.com', 1, 1, 1


;WITh CTE AS
(
    select s.*, 
    ROW_NUMBER() OVER (PARTITION BY email,campaignid
    ORDER BY 
    case 
        when ISSUBSCRIBE = 0 AND ISACTIONBYUSER = 0 THEN 1 
        when ISSUBSCRIBE = 0 AND ISACTIONBYUSER = 1 THEN 1 
        when ISSUBSCRIBE = 1 AND ISACTIONBYUSER = 1 THEN 1 ELSE 2 END, ID DESC) Rn1
    from @test s
)
SELECT * FROM CTE WHERE Rn1 = 1
order by id
rs.
  • 26,707
  • 12
  • 68
  • 90
  • This does not account for a user unsubscribing then subscribing again later. – Tim Lehner Mar 27 '12 at 15:42
  • yes, thanks for catching that, i fixed my query and it should work now – rs. Mar 27 '12 at 16:05
  • i already have id desc in their and my query will account for 'unsubscribing then subscribing again later' – rs. Mar 27 '12 at 16:36
  • This does not account for an EMAIL being subscribed/unsubscribed to multiple CAMPAIGNIDs (maybe "PARTITION BY email, campaignid"?). – Tim Lehner Mar 27 '12 at 16:40
  • @TimLehner you are right, i fixed that and @concrad i don't understand why you are not able to see `ID desc` in my `OVER CLAUSE` – rs. Mar 27 '12 at 16:49
  • @rs sorry I totally derped that – Conrad Frix Mar 27 '12 at 16:54
  • You're still failing to meet this requirement `when the administration desk does an unsubscribe action, it will have priority over a record with (isSubscribe = true and isActionByUser).` just changing your case to `when ISSUBSCRIBE = 0 AND ISACTIONBYUSER = 0 THEN 1 when ISSUBSCRIBE = 0 AND ISACTIONBYUSER = 2 THEN 1 when ISSUBSCRIBE = 1 AND ISACTIONBYUSER = 3 THEN 1 ELSE 4 END` – Conrad Frix Mar 27 '12 at 17:24
  • @concard did you run my query and actually looked at data and results? run my query and see results for c@cc.com and if `(isSubscribe = true and isActionByUser)` has highest ID my guess is it is new subscription and should not be overwritten by `ISSUBSCRIBE = 0 AND ISACTIONBYUSER = 0`, OP was having issues dealing with rows IDs 6 and 7 and my query and Tim's query will handle that – rs. Mar 27 '12 at 18:36
  • @rs. I really appreciate your help. I have created some more complex scenario's to test and your query succeeds in returning the correct records. I also like that your script is very compact. I'm going to use this one. – CyclingFreak Mar 28 '12 at 07:55
1

This is some standard SQL that might get you there, though it's not the prettiest ever:

Updated:

select s.*
from Subscriptions s
    join (
        -- Apply the user unsubscribe logic to get the proper ID
        select case when b.ID is not null and a.ISACTIONBYUSER = 0 then b.ID else a.ID end as ID
        from (
                -- Latest overall
                select ID, EMAIL, CAMPAIGNID,
                    (select ISACTIONBYUSER from Subscriptions where ID = z.ID) as ISACTIONBYUSER
                from (
                    select max(ID) as ID, EMAIL, CAMPAIGNID
                    from Subscriptions a
                    group by EMAIL, CAMPAIGNID
                ) as z
            ) as a
            left join (
                -- Latest user unsubscribe
                select max(ID) as ID, EMAIL, CAMPAIGNID, 1 as ISACTIONBYUSER
                from Subscriptions
                where ISSUBSCRIBE = 0
                    and ISACTIONBYUSER = 1
                group by EMAIL, CAMPAIGNID
            ) as b on a.EMAIL = b.EMAIL
                and a.CAMPAIGNID = b.CAMPAIGNID
    ) as i on s.ID = i.ID

I've updated this to account for this case:

insert into Subscriptions select 8, 'd@dd.com', 1, 1, 1
insert into Subscriptions select 9, 'd@dd.com', 1, 0, 1
insert into Subscriptions select 10, 'd@dd.com', 1, 1, 1
Tim Lehner
  • 14,813
  • 4
  • 59
  • 76