使用Row_Number查找连续的行数


8

我有这列整数表示信号的出现,我试图添加一列来显示连续行的计数

如果我的数据看起来像这样

724
727
728
733
735
737
743
747
749

具有连续行计数列的结果数据将如下所示

724 1
727 1
728 2
729 3
735 1
737 1
743 1
744 2
748 1

我已经使用循环功能完成了此操作,但正在尝试使用cte进行查找。这是我最近尝试的一个示例

DECLARE @d TABLE ( signal INT )
INSERT  INTO @d
        SELECT  724
        UNION
        SELECT  727
        UNION
        SELECT  728
        UNION
        SELECT  729
        UNION
        SELECT  735
        UNION
        SELECT  737
        UNION
        SELECT  743
        UNION
        SELECT  744
        UNION
        SELECT  748 ;
WITH    a AS ( SELECT   signal,
                        ROW_NUMBER() OVER ( ORDER BY signal ) AS marker
               FROM     @d
             ) ,
        b AS ( SELECT   a1.signal,
                        CASE ( a1.signal - a2.signal )
                          WHEN 1 THEN 1
                          ELSE 0
                        END consecutiveMarker
               FROM     a a1
                        INNER JOIN a a2 ON a2.marker = a1.marker - 1
             )
    SELECT  *
    FROM    b

产生这些结果

signal  consecutiveMarker
727 0
728 1
729 1
735 0
737 0
743 0
744 1
748 0

第一个明显的问题是缺少系列中的第一个信号。除非如此,否则我想可以将其传递给在continuousMarker上具有row_number分区的另一个cte。那没有用,因为它将其分区为一个分区。我找不到一种方法来指示分区方法,一个系列与下一个系列是分开的

任何帮助表示赞赏。


1
源数据和预期结果之间似乎不匹配。
马丁·史密斯,

Answers:


16

这种查询的通用名称是“间隙和孤岛”。下面的一种方法。如果源数据中可能有重复项,则可能需要dense_rank而不是row_number

WITH DATA(C) AS
(
SELECT 724 UNION ALL
SELECT 727 UNION ALL
SELECT 728 UNION ALL
SELECT 729 UNION ALL
SELECT 735 UNION ALL
SELECT 737 UNION ALL
SELECT 743 UNION ALL
SELECT 744 UNION ALL
SELECT 747 UNION ALL
SELECT 749
), T1 AS
(
SELECT C,
       C - ROW_NUMBER() OVER (ORDER BY C) AS Grp
FROM DATA)
SELECT C,
       ROW_NUMBER() OVER (PARTITION BY Grp ORDER BY C) AS Consecutive
FROM T1

退货

C           Consecutive
----------- --------------------
724         1
727         1
728         2
729         3
735         1
737         1
743         1
744         2
747         1
749         1

1

在SQL 2012中,您也可以使用LAG和窗口函数来执行此操作,例如

DECLARE @d TABLE ( signal INT PRIMARY KEY) 

INSERT INTO @d 
VALUES
    ( 724 ),
    ( 727 ),
    ( 728 ),
    ( 729 ),
    ( 735 ),
    ( 737 ),
    ( 743 ),
    ( 744 ),
    ( 748 )

SELECT signal
    , 1 + ( SUM( is_group ) OVER ( ORDER BY signal ROWS BETWEEN 1 PRECEDING AND 0 FOLLOWING ) * is_group )
FROM
    (
    SELECT *
        , CASE WHEN LAG(signal) OVER( ORDER BY signal ) = signal - 1 THEN 1 ELSE 0 END is_group
    FROM @d
    ) x

-1

像往常一样,遇到此类问题时,很容易用Java或C ++或C#完成。

如果确实需要在数据库中执行此操作,则可以使用带有快速游标的RDBMS(例如Oracle),编写简单的游标并享受快速性能,而不必编写任何复杂的东西。

如果您需要在T-SQL中执行此操作,并且无法更改数据库设计,则Itzik Ben-Gan在“ MVP Deep Dives vol 1”中编写了一些解决方案,并在有关窗口函数的新书中使用了OLAP函数编写了一些新的解决方案。在SQL 2012中。

或者,您可以向表中添加另一列ContinuousMarker,并在其中存储预先计算的值。我们可以使用约束来确保预先计算的数据始终有效。如果有人感兴趣,我可以解释一下。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.