T-SQL - 持续增加值的下一行

时间:2016-10-31 17:15:51

标签: sql-server tsql

我有一个表描述,我需要从中选择[Value]更大的所有行,例如比前一行的[Value]至少5个点(按[Id]排序)。从[Id] 1的第一行开始,所需的输出将是:

[Id]    [Value]
---------------
  1        1
  4       12
  8       21

代码:

 declare @Data table 
               (
                    [Id] int not null identity(1, 1) primary key,
                    [Value] int not null
               );

 insert into @Data ([Value])
     select 1 [Value]
     union all
     select 5
     union all
     select 3
     union all
     select 12
     union all
     select 8
     union all
     select 9
     union all
     select 16
     union all
     select 21;

select [t1].*
from @Data [t1];

编辑:

所以,基于JNevill和Hogan的答案,我以此结束:

 ;with [cte1]
 as (

    select [t1].[Id], 
    [t1].[Value], 
    cast(1 as int) [rank]
    from @Data [t1]
    where [t1].[Id] = 1

    union all

    select [t2].[Id],
    [t2].[Value],
    cast(row_number() over (order by [t2].id) as int) [rank]
    FROM [cte1] [t1]
    inner join @Data [t2] on [t2].[value] - [t1].[value] > 5
    and [t2].[Id] > [t1].[Id]
    where [t1].[rank] = 1

 )

 select [t1].[Id],
 [t1].[Value]
 from [cte1] [t1]
 where [t1].[rank] = 1;

哪个有效。 Alan Burstein的回答也是正确的(但仅适用于MSSQL 2012+ - 由于LAG fc)。我将做一些性能测试(我在2016年版本上)并且会看到我的真实数据(大约3000万条记录)的性能。

3 个答案:

答案 0 :(得分:3)

如果你在2012年以上,你可以使用LAG,它将提供一个更好的解决方案,递归CTE。我包含您的示例数据,因此您只需复制/粘贴/测试......

-- Your sample data
DECLARE @Data TABLE
(
  Id int not null identity(1, 1) primary key,
  Value int not null
);
insert into @Data ([Value])
  select 1 [Value] union all select 5  union all select 3  union all select 12 union all
  select 8  union all select 9  union all select 16 union all select 21;

-- Solution using window functions
WITH 
prevRows AS
(
  SELECT t1.Id, t1.Value, prevDiff = LAG(t1.Value, 1) OVER (ORDER BY t1.id) - t1.Value
  FROM @Data t1
),
NewPrev AS
(
  SELECT t1.Id, t1.Value, NewDiff = Value - LAG(t1.Value,1) OVER (ORDER BY t1.id)
  FROM prevRows t1
  WHERE prevDiff <= -5 OR prevDiff IS NULL
)
SELECT t1.Id, t1.Value
FROM NewPrev t1
WHERE NewDiff >= 5 OR NewDiff IS NULL;

答案 1 :(得分:1)

我认为解决此问题的最佳方法是使用递归CTE。递归CTE是一种特殊类型的CTE,它可以引用自身。它由两部分组成。

  1. 递归种子/锚,它建立了递归的开始。在您的情况下,记录ID = 1.

  2. 递归术语/成员,它是通过CTE名称引用自身的语句。在这里,我们根据按升序排序的ID,从上一个找到的记录中提取大于5的下一条记录。

  3. 代码:

    WITH RECURSIVE recCTE AS
    (
        /*Select first record for recursive seed/anchor*/
        SELECT 
            id, 
            value, 
            cast(1 as INT) as [rank]
        FROM table 
        WHERE id = 1
    
        UNION ALL
    
        /*find the next value that is more than 5 from the current value*/
        SELECT
            table.id, 
            table.value
            ROW_NUMBER() OVER (ORDER BY id) 
        FROM
            recCTE INNER JOIN table 
                ON table.value - recCTE.value > 5
                   AND table.id > recCTE.id
        WHERE recCTE.[rank]=1
    )
    
    SELECT id, value FROM recCTE;
    

    我已经使用Row_Number()窗口函数来按ID排序升序查找匹配记录的等级。使用递归术语中的WHERE子句,我们只获取第一个找到的记录,该记录比先前找到的记录多5个。然后我们进入下一个递归步骤。

答案 2 :(得分:0)

您可以使用递归CTE

来完成
with find_values as
(
   -- Find first value
   SELECT Value 
   FROM @Table
   ORDER BY ID ASC
   FETCH FIRST 1 ROW ONLY

   UNION ALL

   -- Find next value
   SELECT Value 
   FROM @Table
   CROSS JOIN find_values
   WHERE Value >= find_values.Value + 5
   ORDER BY ID ASC
   FETCH FIRST 1 ROW ONLY
)
SELECT *
FROM find_values