SQL: получить последнюю (не) подписку из таблицы - PullRequest
2 голосов
/ 27 марта 2012

У меня есть следующая таблица:

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

В этой таблице хранятся все действия по подписке и отмене подписки для кампаний для пользователя.Эти действия могут быть выполнены самим пользователем (isActionByUser = true) или администрацией (isActionByUser = false).

Мне нужно получить последнее действие, чтобы определить, подписан ли пользователь или отписан от него.Но помните, что когда пользователь выполнил действие по отмене подписки в кампании, он будет иметь приоритет над другими действиями по подписке со стороны администрации.

Я нашел хорошее решение , чтобы получитьпоследние записи сгруппированы по EMAIL и CAMPAIGNID.Но я не могу понять, как я включаю требование, что isActionByUser = true, имеет абсолютный приоритет над записями с isActionByUser = false.Также: когда администрация выполняет действие по отмене подписки, она будет иметь приоритет над записью с (isSubscribe = true и isActionByUser).

Пример данных:

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

Ожидаемый результат будет:

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

С помощью следующего запроса

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

У меня следующий результат:

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

Другой подход:

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
            )
        )   

Это приведет к следующему результату:

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

Что также неверно.И я боюсь, что производительность будет большой проблемой при таком подходе.

Так есть какие-нибудь идеи, как я могу решить это?

Ответы [ 3 ]

3 голосов
/ 27 марта 2012

Хорошо, попробуйте следующий запрос:

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
2 голосов
/ 27 марта 2012

Попробуйте: [Обновлено для обработки отписавшихся и подписавшихся пользователей]

    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
1 голос
/ 27 марта 2012

Это какой-то стандартный SQL, который может привести вас туда, хотя и не самый красивый:

Обновлен:

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

Я обновил это для учета этого случая:

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
...