2016-10-31 18 views
0

我有下面描述的表,我需要從中選擇[Value]更大的所有行,例如至少比前一行中的[Value]要多5點(按[Id ])。與[ID] 1的第一行開始,所需的輸出將是:T-SQL - 具有較大值的下一行,連續

[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版),並會看到我的真實數據(大約有3千萬條記錄)的性能。

+0

爲什麼'16'不是結果的一部分? '16-9> 5' –

+0

因爲它必須與之前創建的Value進行比較,它是12. – paYa

+0

對於'16',當我們通過'ID'命令時,以前的值是'9' –

回答

1

我相信最好的方法是使用遞歸CTE。遞歸CTE是一種特殊類型的CTE,它指向自身。它由兩部分組成。

  1. 建立遞歸開始的遞歸種子/錨點。在你的情況下,記錄ID = 1。

  2. 遞歸術語/成員是通過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個記錄。然後我們進入下一個遞歸步驟。

+0

你不需要行號 - 只要TOP 1可以工作 - 但是你不能在沒有子查詢的情況下在where子句中使用[rank]。 – Hogan

+0

也爲什麼你將ROW_NUMBER()轉換爲int? – Hogan

+0

我的印象是,SQL Server中的遞歸術語中不允許使用「TOP」關鍵字。這將是一個更好的方式來處理它。 – JNevill

1

您可以用遞歸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 
+0

子查詢存在問題:子查詢中不允許遞歸引用。 – paYa

+0

@paYa - 立即嘗試。 – Hogan

+0

那麼,TOP是不允許在CTE的 – paYa

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; 
+0

我不認爲這會工作 - LAG只回顧一行,但他需要回頭看1,2和更多 – Hogan

+0

我現在試過它,它的工作。 – paYa

+0

哎呀,我現在可以看到了經過一些其他的測試後,這是行不通的,就像Hogan說的那樣。 – paYa