6

对于此示例,假设我有一个包含两个字段的表,AREA varchar(30)并且OrderNumber INT.

该表有以下数据

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 上执行此操作?

4

3 回答 3

9

一种方法是使用遍历每一行的递归 CTE。如果该行符合条件(增加同一区域的订单号),则将链长度增加一。如果没有,你开始一个新的链:

; 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

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

SQL Fiddle 上的实时示例。

于 2013-03-12T18:47:05.540 回答
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]

因此,如果我按我的 ordernumber 排序一组行号,并按我的事件时间排序另一组行号,这两个数字之间的差异将始终相同,只要它们的顺序相同。

然后,您可以获得按这些差异分组的计数,然后提取最大的计数以获得您需要的。

编辑:...忽略第一次编辑,我急于求成。

于 2013-03-12T21:08:06.207 回答
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

我像这样实现了上述内容:

WITH enumerated AS (
  SELECT
    *,
    row = ROW_NUMBER() OVER (PARTITION BY AREA ORDER BY eventtime)
  FROM atable
),
signed AS (
  SELECT
    this.eventtime,
    this.AREA,
    this.row,
    sgn = SIGN(this.OrderNumber - COALESCE(last.OrderNumber, 0))
  FROM      enumerated AS this
  LEFT JOIN enumerated AS last
    ON this.AREA = last.AREA
   AND this.row  = last.row + 1
),
partitioned AS (
  SELECT
    AREA,
    sgn,
    grp = row - ROW_NUMBER() OVER (PARTITION BY AREA, sgn ORDER BY eventtime)
  FROM signed
)
SELECT DISTINCT
  AREA,
  LongestIncSeq = MAX(COUNT(*)) OVER (PARTITION BY AREA)
FROM partitioned
WHERE sgn = 1
GROUP BY
  AREA,
  grp
;

可以在此处找到 SQL Fiddle 演示。

于 2013-03-18T10:37:46.163 回答