【问题标题】:Getting all consecutive rows differing by certain value?获取所有连续行的不同值?
【发布时间】:2013-05-07 17:39:39
【问题描述】:

我正在努力解决这个问题,因为它涉及到连续行的比较。我正在尝试对相差某个数字的值进行分组。例如,假设我有这张表:

CREATE TABLE #TEMP (A int, B int)

-- Sample table
INSERT INTO #TEMP VALUES 
(3,1), 
(3,2), 
(3,3),
(3,4),
(5,1),
(6,1),
(7,2),
(8,3),
(8,4),
(8,5),
(8,6)

SELECT * FROM #TEMP

DROP TABLE #TEMP

假设我必须将所有相差 1 的值分组为具有相同 A 的值。然后我试图获得这样的输出:

A B GroupNo
3 1 1
3 2 1
3 3 1
3 4 1
5 1 2
6 1 3
7 2 4
8 3 5
8 4 5
8 5 5
8 6 5

(3,1) (3,2) (3,3) (3,4)(8,3) (8,4) (8,5) (8,6) 已被放入同一组,因为它们的值相差 1。我将首先展示我的尝试:

CREATE TABLE #TEMP (A int, B int)

-- Sample table
INSERT INTO #TEMP VALUES 
(3,1), (3,2), (3,3), (3,4), (5,1), (6,1), (7,2),
(8,3), (8,4), (8,5), (8,6)

-- Assign row numbers and perform a left join
-- so that we can compare consecutive rows
SELECT ROW_NUMBER() OVER (ORDER BY A ASC) ID, * 
INTO #TEMP2
FROM #TEMP

;WITH CTE AS
(
    SELECT X.A XA, X.B XB, Y.A YA, Y.B YB
    FROM #TEMP2 X
    LEFT JOIN #TEMP2 Y
    ON X.ID = Y.ID - 1
    WHERE X.A = Y.A AND
    X.B = Y.B - 1
)
SELECT XA, XB
INTO #GROUPS
FROM CTE
UNION 
SELECT YA, YB
FROM CTE
ORDER BY XA ASC 

-- Finally assign group numbers
SELECT X.XA, X.XB, Y.GID
FROM #GROUPS X
INNER JOIN
(SELECT XA, ROW_NUMBER() OVER (ORDER BY XA ASC) GID
    FROM #GROUPS Y
    GROUP BY XA
) Y
ON X.XA = Y.XA

DROP TABLE #TEMP
DROP TABLE #TEMP2
DROP TABLE #GROUPS

我将在一个大表(大约 3000 万行)上执行此操作,因此我希望有更好的方法来处理任意值(例如,不仅相差 1,还可以是 2 或 3我将在稍后将其合并到一个程序中)。关于我的方法是否没有错误以及是否可以改进的任何建议?

【问题讨论】:

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


    【解决方案1】:

    对于它们相差一个的情况,您可以使用

    ;WITH T AS
    (
    SELECT *,
           B - DENSE_RANK() OVER (PARTITION BY A ORDER BY B) AS Grp
    FROM #TEMP
    )
    SELECT A,
           B,
           DENSE_RANK() OVER (ORDER BY A,Grp) AS GroupNo
    FROM T
    ORDER BY A, Grp
    

    更一般的

    DECLARE @Interval INT = 2
    
    ;WITH T AS
    (
    SELECT *,
           B/@Interval - DENSE_RANK() OVER (PARTITION BY A, B%@Interval ORDER BY B) AS Grp
    FROM #TEMP
    )
    SELECT A,
           B,
           DENSE_RANK() OVER (ORDER BY A, B%@Interval,Grp) AS GroupNo
    FROM T
    ORDER BY A, GroupNo
    

    【讨论】:

    • +1 完美!一个快速澄清:是否有一种自然的方法可以将其扩展到我看到的情况小于或等于而不是绝对差异?也就是说,对于@Interval=2 的情况,它还将相差一个的值放入同一组中。所以在这种情况下,它将(8,3) (8,4) (8,5) (8,6) 归为一组。
    • @Legend - 将不得不考虑那个!我认为 Mikael 的答案是基于小于或等于假设。
    • 是的。我是这么想的。您的版本看起来有点短而优雅:) 但是,感谢您的帮助。我今天学到了一项新技术!
    【解决方案2】:
    declare @Diff int = 1
    
    ;with C as
    (
      select A, 
             B,
             row_number() over(partition by A order by B) as rn
      from #TEMP
    ),
    R as
    (
      select C.A,
             C.B,
             1 as G,
             C.rn
      from C
      where C.rn = 1
      union all
      select C.A,
             C.B,
             G + case when C.B-R.B <= @Diff 
                   then 0
                   else 1
                 end,
             C.rn
      from C
        inner join R
           on R.rn + 1 = C.rn and
              R.A = C.A       
    )
    select A,
           B,
           dense_rank() over(order by A, G) as G
    from R
    order by A, G
    

    【讨论】:

    • +1 感谢您抽出宝贵时间!这就像我想要的那样工作。我会检查这个的性能并回来。我的方法几乎相似,但我担心多个连接。
    猜你喜欢
    • 2022-01-12
    • 1970-01-01
    • 1970-01-01
    • 2019-07-28
    • 2016-02-05
    • 1970-01-01
    • 2021-04-08
    • 2020-06-29
    • 2019-11-13
    相关资源
    最近更新 更多