6

我在将相同类型的记录与连续序列合并并从合并记录中计算完整序列时遇到问题。

排序应该基于 ID 完成,因为当序列达到 100 时,序列可能会翻转到 0。请参阅输入/输出示例中的最后一个条目。

是否可以有一个如下所列的输入,并有一个产生输出的查询,该输出也在 SQL Server 2012 中列出?

输入

Id     Type     Begin     End
-----------------------------
1      1        10        20
2      1        21        23
3      2        24        28
4      1        29        40
5      2        41        47
6      2        48        50
7      2        75        80
8      1        81        100
9      1        0         10
10     1        11        20
11     1        21        5
12     1        5         6

输出

FromId     ToId     Type     Begin     End    Length
----------------------------------------------------
1          2        1        10        23     13 (23-19)
3          3        2        24        28     4  (28-24)
4          4        1        29        40     11 (40-29)
5          6        2        41        50     9  (50-41)
7          7        2        75        80     5  (80 - 75)
8          12       1        81        20     227*

*(100-81) + 10 + (100-11 + 20) + (100-21 + 5) + 1 -> 序列的翻转

编辑

请注意,源中的第 6 行和第 7 行没有合并,因为它们不连续。第 6 行以 50 结尾,第 7 行以 75 开头。只有相同类型的连续行需要合并。

4

3 回答 3

3

您最后一行的 Begin = 10 与其他人不遵循相同的规则。在我的示例中,我将其更新为 11。希望这会有所帮助。

SQL 提琴手

WITH typeRowNum AS (  
  SELECT *, ROW_NUMBER() OVER (ORDER BY Id ASC) AS rownum
  FROM tblType
)
,rw AS (
  SELECT t1.*, 
    CASE WHEN t1.[type] = t2.[type] and ( t1.[Begin] = t2. [end] + 1 OR t1.[Begin] + 100 = t2.[end])
      THEN -1 
      ELSE t1.rownum 
    END AS group_id
  FROM typeRowNum t1
  LEFT JOIN typeRowNum t2 
    ON t2.rownum = t1.rownum - 1
 )
, cte AS (
SELECT *,
  new_end = ISNULL(
    (SELECT MIN(rownum) - 1 FROM rw r2 WHERE r2.rownum > r1.rownum and r2.group_id > r1.group_id),
    (SELECT MAX(rownum) FROM rw)
  )
FROM rw r1
WHERE r1.group_id > 0
 )
select 
  c1.id,c1.type,c1.[begin],c2.[end]
 ,[length] = (SELECT SUM((r.[end]  - r.[Begin]
                  + CASE WHEN r.[end] < r.[Begin] THEN 100 ELSE 0 END 
                  + CASE WHEN (r.group_id = -1) AND (r.[Begin] < r.[End]) THEN 1 ELSE 0 END)
                 ) 
             FROM rw r WHERE r.rownum  BETWEEN c1.[rownum] AND c2.[rownum])  
FROM cte c1
LEFT JOIN rw c2
  ON c1.new_end = c2.rownum

更新:如果您有 NULL 值,很可能您在 [Id] 列中有一些已终止的值。相反,您可以使用 Row_Number 来加入。我在上面更新了我的答案。

于 2013-05-13T14:09:47.427 回答
1

嗯...非常有趣的任务我最终得到了以下结果

Type    IntervalBegin   CurrEnd
1   10  20
1   NULL    23
2   24  28
1   29  40
2   41  47
2   NULL    50
2   75  80
1   81  100
1   NULL    10
1   10  20

但我仍然对汇总收到的结果感到困惑......

查询如下

DECLARE @MyTable TABLE ([Id] INT, [Type] INT, [Begin] INT, [End] INT)

INSERT INTO @MyTable([Id], [Type], [Begin], [End] )
VALUES
    (1, 1, 10, 20),
    (2, 1, 21, 23),
    (3, 2, 24, 28),
    (4, 1, 29, 40),
    (5, 2, 41, 47),
    (6, 2, 48, 50),
    (7, 2, 75, 80),
    (8, 1, 81, 100),
    (9, 1, 0, 10),
    (10, 1, 10, 20)

    SELECT 
        [Type],
        CASE
            WHEN ShouldCompareWithPrevious = 1 AND PrevBegin IS NULL THEN CurrBegin 
            WHEN ShouldCompareWithPrevious = 1 AND PrevEnd = 100 AND CurrBegin = 0 THEN NULL
            WHEN ShouldCompareWithPrevious = 1 AND PrevEnd + 1 <> CurrBegin THEN CurrBegin
            WHEN ShouldCompareWithPrevious = 0 THEN CurrBegin 
            ELSE NULL
        END IntervalBegin,
        CurrEnd
    FROM
    (

        SELECT t1.[Id], t2.[Id] t2Id,
            t1.[Type], t2.[Type] t2Type,
            (
                CASE
                 WHEN t2.[Type]  IS NULL THEN 0
                 WHEN t2.[Type] = t1.[Type] THEN 1
                 ELSE
                    0
                END
            ) AS ShouldCompareWithPrevious, 
            t1.[Begin] CurrBegin,
            t1.[End] CurrEnd,
            t2.[Begin] PrevBegin, 
            t2.[End] PrevEnd
        FROM @MyTable t1
        LEFT OUTER JOIN @MyTable t2
            ON t1.Id = t2.Id + 1
    ) intermideate
于 2013-05-14T14:08:52.110 回答
0

这个问题通常可以通过这样的递归来解决:

create table #t ([Id] int, [Type] int, [Begin] int, [End] int);
insert into #t values (1,1,10,20),(2,1,21,23),(3,2,24,28),(4,1,29,40),
(5,2,41,47),(6,2,48,50),(7,2,75,80),(8,1,81,100),(9,1,0,10),(10,1,10,20);

with cRek as (
    -- records with no followup
    select  t.[Type], FromId = t.[Id], ToId = t.[Id], 
            t.[Begin], t.[End], [Length] = t.[End]-t.[Begin]+1
    from    #t t
    left join #t tf
        on  tf.[Type] = t.[Type]
        and tf.[Begin] = (t.[End]+1)%101
        and tf.[Id] > t.[Id]
    where   tf.[Id] is null

    union all

    -- previous record
    select  t.[Type], FromId = t.[Id], ToId = tf.[ToId], 
            t.[Begin], tf.[End], [Length] = tf.[Length]+t.[End]-t.[Begin]+1
    from    #t t
    inner join cRek tf
        on  tf.[Type] = t.[Type]
        and tf.[Begin] = (t.[End]+1)%101
        and tf.[FromId] > t.[Id]
)
select  *
from    cRek r
where   FromId = 
        (select min(x.FromId)
        from    cRek x
        where   x.[Type]=r.[Type] and x.[ToId]=r.[ToId])
order by ToId;

drop table #t;

您的样本有一些小缺陷:

  • 100 后面是 0,因此您有 101 个不同的元素(模 101!)
  • Id 10 不是 Id 9 的后续,因为它们都包含元素 10
  • 从 10 到 23 的长度是 14,因为它是一个包含区间

HTH,曼弗雷德

于 2013-05-15T06:03:24.107 回答