我有一个表描述,我需要从中选择[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万条记录)的性能。
答案 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,它可以引用自身。它由两部分组成。
递归种子/锚,它建立了递归的开始。在您的情况下,记录ID = 1.
递归术语/成员,它是通过CTE名称引用自身的语句。在这里,我们根据按升序排序的ID,从上一个找到的记录中提取大于5的下一条记录。
代码:
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