SQL:从表中获取最新(un)订阅

时间:2012-03-27 14:30:41

标签: sql sql-server sql-server-2008 sql-server-2008-r2

我有下表:

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

此表存储针对用户的广告系列的所有订阅和取消订阅操作。这些操作可以由用户自己(isActionByUser = true)或管理台(isActionByUser = false)完成。

我需要获取最后一个操作来确定用户是订阅还是取消订阅。但请记住,当用户从广告系列中取消订阅操作时,管理台将优先处理其他订阅操作。

我找到了nice solution来获取按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 个答案:

答案 0 :(得分:3)

好的,请尝试以下查询:

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

答案 1 :(得分:2)

试试这个:[更新以处理取消订阅和订阅的用户]

    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

答案 2 :(得分:1)

这是一些标准的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