2013-03-12 74 views
5

對於本示例,假設我有一個包含兩個字段的表,即AREA varchar(30)OrderNumber INT查找SQL中連續遞增數字的最長序列

該表具有如下的數據

AREA  | OrderNumber 
Fontana |  32 
Fontana |  42 
Fontana |  76 
Fontana |  12 
Fontana |  3 
Fontana |  99 
RC  |  32 
RC  |  1 
RC  |  8 
RC  |  9 
RC  |  4 

我想返回

我想返回的結果是爲每個區域增加連續值的最長長度。對於Fontana it is 3 (32, 42, 76)For RC it is 2 (8,9)

AREA | LongestLength 
Fontana |   3 
RC  |   2 

我該怎麼做MS Sql 2005?

+5

增加什麼時候點什麼? SQL中沒有行的固有順序,您向我們顯示的兩列中都沒有適合的候選項。 – 2013-03-12 18:08:31

+0

對不起,我忘了添加eventtime列,這是一個日期時間,這將是我們的訂單。如果我通過AREA訂購OrderNumber,那麼產量會如此。然後,我想找到連續增加OrderNumbers – 2013-03-12 18:12:30

+1

@Pat裏克艾倫最長的塊 - 請根據您的上述澄清更新您的文章。 – 2013-03-12 18:23:31

回答

8

一種方法是使用跨每行的遞歸CTE。如果該行符合條件(相同區域的遞增順序號),則將鏈長增加1。如果沒有,你開始一個新的鏈:

; with numbered as 
     (
     select row_number() over (order by area, eventtime) rn 
     ,  * 
     from Table1 
     ) 
,  recurse as 
     (
     select rn 
     ,  area 
     ,  OrderNumber 
     ,  1 as ChainLength 
     from numbered 
     where rn = 1 
     union all 
     select cur.rn 
     ,  cur.area 
     ,  cur.OrderNumber 
     ,  case 
       when cur.area = prev.area 
        and cur.OrderNumber > prev.OrderNumber 
        then prev.ChainLength + 1 
       else 1 
       end 
     from recurse prev 
     join numbered cur 
     on  prev.rn + 1 = cur.rn 
     ) 
select area 
,  max(ChainLength) 
from recurse 
group by 
     area 

Live example at SQL Fiddle.

的另一種方法是使用查詢來查找「休息」,也就是說,爲了結束越來越多的順序排爲同一地區。斷點之間的行數是長度。

; with numbered as 
     (
     select row_number() over (order by area, eventtime) rn 
     ,  * 
     from Table1 t1 
     ) 
     -- Select rows that break an increasing chain 
,  breaks as 
     (
     select row_number() over (order by cur.rn) rn2 
     ,  cur.rn 
     ,  cur.Area 
     from numbered cur 
     left join 
       numbered prev 
     on  cur.rn = prev.rn + 1 
     where cur.OrderNumber <= prev.OrderNumber 
       or cur.Area <> prev.Area 
       or prev.Area is null 
     ) 
     -- Add a final break after the last row 
,  breaks2 as 
     (
     select * 
     from breaks 
     union all 
     select count(*) + 1 
     ,  max(rn) + 1 
     ,  null 
     from breaks 
     ) 
select series_start.area 
,  max(series_end.rn - series_start.rn) 
from breaks2 series_start 
join breaks2 series_end 
on  series_end.rn2 = series_start.rn2 + 1 
group by 
     series_start.area 

Live example at SQL Fiddle.

+0

這工作!非常感謝。 – 2013-03-12 19:27:30

+0

我正在嘗試在真實數據上實現第二個解決方案,但真正的數據有很多行,然後出現錯誤「在語句完成之前,最大遞歸100已耗盡」。 *我打算嘗試第一個解決方案,但我的真實數據實際上包含這些行Eventtime,PublisherID,SubID,CampaignID,Userip,Userip是原始示例中的訂單號。如何編輯第一個解決方案以添加更多「區域」字段。 – 2013-03-12 19:48:39

+0

在查詢的末尾添加'option(maxrecursion 0)' – Andomar 2013-03-12 19:50:43

0

您可以通過ROW_NUMBER()做一些數學來找出你有連續的項。

下面的代碼示例:

;WITH rownums AS 
(
    SELECT [area], 
     ROW_NUMBER() OVER(PARTITION BY [area] ORDER BY [ordernumber]) AS rid1, 
     ROW_NUMBER() OVER(PARTITION BY [area] ORDER BY [eventtime]) AS rid2 
    FROM SomeTable 
), 
    differences AS 
(
    SELECT [area], 
     [calc] = rid1 - rid2 
    FROM rownums 
), 
    summation AS 
(
    SELECT [area], [calc], COUNT(*) AS lengths 
    FROM differences 
    GROUP BY [area], [calc] 
) 
SELECT [area], MAX(lengths) AS LongestLength 
FROM differences 
JOIN summation 
    ON differences.[calc] = summation.[calc] 
    AND differences.area = calc.area 
GROUP BY [area] 

所以,如果我做一組由我的訂單號碼和我的事件時另一套行號的命令行號,這兩個數字之間的差異將永遠是同樣,只要他們的訂單是一樣的。

然後,您可以得到一個按這些差異分組的計數,然後拉出最大的數量來獲得您所需要的數量。

編輯:... 忽略第一次編輯,我得到衝。

0

你不解釋爲什麼RC的最長序列不包括1,而Fontana的包括32.我認爲,1被排除,因爲它是一個減少:它在32之後。然而,Fontana的32是第一個這個小組中有一件事,我有兩個想法如何解釋爲什麼它被認爲是增加。這要麼是因爲它是該組的第一個項目,或者因爲它是也是正數(即好像在0之後,因此增加)。

爲了這個答案的目的,我假設後者,即一組的第一項是增加,如果它是積極的。下面的腳本實現了以下想法:

  1. 枚舉行每AREA組在eventtime列,你幾乎忘了提及的順序。

  2. 將枚舉集合加入其自身,以將每一行與其前任進行鏈接。

  3. 獲得該行與其上一個值之間的差異(將後者默認爲0)的符號。此時問題變成了之一。

  4. 按照#3中確定的符號對每個AREA組進行分區並枚舉每個子組的行。

  5. 找到#1與#4中找到的行號之間的區別。這將是識別單個條紋的標準(連同AREA)。

  6. 最後,將結果分組爲AREA,來自#3的符號和來自#5的結果對行進行計數並獲得每AREA的最大計數。

我實現了上面這樣的:

​​

一個SQL小提琴演示可以發現here