Sql server 在SQL Server中合并/扩展具有相同类型的记录

Sql server 在SQL Server中合并/扩展具有相同类型的记录,sql-server,tsql,Sql Server,Tsql,我在将相同类型的记录与连续序列合并以及从合并的记录计算完整序列时遇到问题 排序应在基础ID上进行,因为序列可能在达到100时滚动到0。请参阅输入/输出示例中的最后一项 是否可以使用下面列出的输入,并使用查询生成SQL Server 2012中下面列出的输出 输入 输出 FromId ToId Type Begin End Length ---------------------------------------------------- 1

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

排序应在基础ID上进行,因为序列可能在达到100时滚动到0。请参阅输入/输出示例中的最后一项

是否可以使用下面列出的输入,并使用查询生成SQL Server 2012中下面列出的输出

输入

输出

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开头。只有具有相同类型的连续行需要合并。

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


更新:若你们有空值,那个么很可能你们在[Id]列中有一些不连续的值。相反,您可以使用Row_Number加入。我更新了上面的答案。

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

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

此问题通常可以通过如下递归解决:

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,因为它是一个包含区间
嗯,曼弗雷德

任务上下文中的[类型]验证是什么?它是[1..2]还是INT?它是一个INT,现在只能保存值1或2。很好,但我会使用模100来覆盖所有情况。当您得到t1.begin=100和t2.end=100时,它将无法显示连接。这可以很好地工作,但很抱歉,因为整个序列可以滚动多次。这个案例目前还没有涉及:@EricZ,我得到了一些奇怪的空结果,集合更大;类似于yaugen在下面提到的内容。我需要对此进行调查。@Frank,如果您的结果为空,很可能意味着您的[Id]列不总是在序列中。请检查我上面更新的答案。这一个几乎有效。我现在的问题是,如果序列滚动多次,长度总持续时间字段的计算不正确。它应该-如果您在一个Id内滚动,则只会出现问题。这就是我添加在单个Id内计算的长度的原因。我尝试了sql FIDLE中的查询,但出现了一些错误;请检查此链接:我似乎发现了问题:-长度字段不应始终为End-Begin,因为如果序列滚动,它应为Max sequence=100-Begin+End。这解决了错误持续时间的问题。
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
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;