6

同じタイプのレコードを連続したシーケンスとマージし、マージされたレコードから完全なシーケンスを計算する際に問題があります。

配列は 100 に達すると 0 にロールオーバーする可能性があるため、ID に基づいて順序付けを行う必要があります。入力/出力の例の最後のエントリを参照してください。

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 -> seq のロールオーバー

編集

ソースの行 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 を使用して JOIN することができます。上記の回答を更新しました。

于 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 に答える