使用上一个值的CTE在单个表中获得前1名

时间:2014-05-29 07:15:08

标签: sql sql-server sql-server-2008 tsql

;WITH  CTE
          AS ( SELECT   rownum = ROW_NUMBER() OVER ( ORDER BY sd.slogid desc) ,
                        sd.slogid ,
                        sd.sid ,
                        sd.statusid
               FROM     statusdetails sd
             )
    SELECT TOP ( 1 )
            cte.slogid,
            CTE.statusid,
            cte.sid ,
            nex.stausid NextValue
    FROM    CTE
            LEFT JOIN CTE prev ON prev.rownum = CTE.rownum - 1
            LEFT JOIN CTE nex ON nex.rownum = CTE.rownum + 1
    WHERE   CTE.statusid <> nex.statusid

我的n数量为sid,并希望获得每个群组top 1

示例数据:

 Slogid        Sid      Statusid

186877          98929         105

186826          98929         105

186821          98929         104

我希望得到slogid - 186826,当状态发生变化时,我想从每个Sid组获取状态。

3 个答案:

答案 0 :(得分:2)

;WITH  CTE AS 
( SELECT   ROW_NUMBER() OVER (PARTITION BY sid ORDER BY sd.slogid desc) RN,
                        sd.slogid ,
                        sd.sid ,
                        sd.statusid
               FROM     statusdetails sd
             )
    SELECT TOP ( 1 )
            cte.slogid,
            CTE.statusid,
            cte.sid ,
            nex.stausid NextValue
    FROM    CTE
            LEFT JOIN CTE prev ON prev.rownum = CTE.rownum - 1
            LEFT JOIN CTE nex ON nex.rownum = CTE.rownum + 1
    WHERE   CTE.statusid <> nex.statusid
    AND RN = 1;

答案 1 :(得分:2)

您似乎需要按sid对row_number函数进行分区,以确保状态更改链接到同一sid

rownum = ROW_NUMBER() OVER (PARTITION BY `sid` ORDER BY sd.slogid desc) ,

假设这就是你想要的,那么你在这里错过了一个JOIN条件:

FROM    CTE
        LEFT JOIN CTE prev ON prev.rownum = CTE.rownum - 1
        LEFT JOIN CTE nex ON nex.rownum = CTE.rownum + 1

您还需要加入sid,否则您将获得交叉加入,因此您的加入应该是:

FROM    CTE
        LEFT JOIN CTE prev ON prev.sid = CTE.sid AND prev.rownum = CTE.rownum - 1
        LEFT JOIN CTE nex ON nex.sid = CTE.sid AND nex.rownum = CTE.rownum + 1

我不知道为什么你加入到之前,因为你从来没有在选择中实际使用它。我个人更倾向于使用APPLY来获取以前的状态:

SELECT  sd.slogid ,
        sd.sid,
        sd.statusid,
        NextValue = n.statusid
FROM    statusdetails AS sd
        CROSS APPLY
        (   SELECT  TOP 1 n.statusid
            FROM    statusdetails AS n
            WHERE   n.sid = sd.sid
            AND     n.slogid > sd.slogid
            ORDER BY n.slogid
        ) n
WHERE   sd.statusid != n.statusid;

最后,我不确定你是否想要每个sid的前1个记录,或者只是最顶层的记录。在您的查询中,您使用TOP而没有order by子句,这不是确定性的,所以不应该这样做。如果你想要每个sid的前1名,你需要使用ROW_NUMBER来获得这个:

WITH CTE AS
(   SELECT  sd.slogid ,
            sd.sid,
            sd.statusid,
            NextValue = n.statusid,
            RowNum = ROW_NUMBER() OVER(PARTITION BY sd.sid ORDER BY sd.slogid DESC)
    FROM    statusdetails AS sd
            CROSS APPLY
            (   SELECT  TOP 1 n.statusid
                FROM    statusdetails AS n
                WHERE   n.sid = sd.sid
                AND     n.slogid > sd.slogid
                ORDER BY n.slogid
            ) n
    WHERE   sd.statusid != n.statusid
)
SELECT  slogid, sid, statusid, NextValue
FROM    CTE
WHERE   RowNum = 1;

<强> Example on SQL Fiddle

为了完整起见,这是您的查询(使用ROW_NUMBER获取下一个状态)并进行了更正:

WITH CTE AS 
(   SELECT  rownum = ROW_NUMBER() OVER (PARTITION BY sd.sid ORDER BY sd.slogid desc) ,
            sd.slogid ,
            sd.sid ,
            sd.statusid
    FROM    statusdetails sd
), CTE2 AS
(   SELECT  cte.slogid,
            CTE.statusid,
            cte.sid ,
            NextValue = nex.statusid,
            RowNum = ROW_NUMBER() OVER(PARTITION BY cte.sid ORDER BY cte.slogid DESC)
    FROM    CTE
            LEFT JOIN CTE nex 
                ON nex.sid = CTE.sid
                AND nex.rownum = CTE.rownum + 1
    WHERE   CTE.statusid <> nex.statusid
)
SELECT  slogid, statusid, sid, NextValue
FROM    CTE2
WHERE   RowNum = 1;

<强> Example on SQL Fiddle

答案 2 :(得分:0)

试试这个

;WITH  CTE AS 
(SELECT   
    rownum = ROW_NUMBER() OVER ( ORDER BY sd.slogid desc) ,
    grouprownum = ROW_NUMBER() OVER ( PARTITION BY sd.[sid] ORDER BY sd.slogid desc) 
    sd.slogid ,
    sd.sid ,
    sd.statusid
 FROM     
    statusdetails sd          
)
    SELECT 
            cte.slogid,
            CTE.statusid,
            cte.sid ,
            nex.stausid NextValue
    FROM    CTE
            LEFT JOIN CTE prev ON prev.rownum = CTE.rownum - 1
            LEFT JOIN CTE nex ON nex.rownum = CTE.rownum + 1
    WHERE   CTE.statusid <> nex.statusid
            AND CTE.grouprownum=1