【问题标题】:Merging/extending records with identical type in SQL Server在 SQL Server 中合并/扩展具有相同类型的记录
【发布时间】:2013-05-04 22:44:26
【问题描述】:

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

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

【问题讨论】:

  • 任务上下文中的 [Type] 真实性是什么?是 [1..2] 还是 INT?
  • 它是一个 int,(现在)只能保存值 1 或 2。

标签: sql-server tsql


【解决方案1】:

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

SQL Fiddler

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 来加入。我更新了上面的答案。

【讨论】:

  • 相当不错,但我会使用模 100 来涵盖所有情况。当您获得 t1.begin=100 和 t2.end=100 时,它将无法显示连接。
  • 这很好用但是(抱歉),因为总序列可以翻转多次。此案例目前未涵盖:( sqlfiddle.com/#!6/8fa0d/1
  • @EricZ,我在较大的集合中得到了一些奇怪的空结果;类似于 yaugen 下面提到的。我需要对此进行调查。
  • @Frank,如果您有空结果,很可能这意味着您的 [Id] 列并不总是按顺序排列。请检查我上面的更新答案。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

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

    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,曼弗雷德

    【讨论】:

    • 这个工作几乎我现在遇到的问题是,如果序列翻转(多次),长度(总持续时间)字段的计算不正确。跨度>
    • 应该 - 如果您在一个 ID 内有翻转,那只是一个问题。这就是我添加在单个 ID 中计算的长度的原因。
    • 我在 sql fiddle 中尝试了查询,但出现了一些错误;请查看此链接:sqlfiddle.com/#!6/fcfb1/2/0
    • 我似乎找到了问题:) - 长度字段不应该总是结束 - 开始,因为如果序列翻转它应该是(最大序列(=100) - 开始)+结束.这解决了错误持续时间的问题。
    猜你喜欢
    • 1970-01-01
    • 2014-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多