Выберите непрерывные диапазоны из таблицы - PullRequest
3 голосов
/ 09 февраля 2012

Мне нужно извлечь непрерывные диапазоны из таблицы на основе последовательных чисел (столбец N) и той же «категории», к которой относятся эти числа (столбец C ниже). Графически это выглядит так:

 N  C  D
--------
 1  x  a           C  N1  N2  D1  D2
 2  x  b          ------------------
 3  x  c           x   1   4   a   d     (continuous range with same N)
 4  x  d    ==>    x   6   7   e   f     (new range because "5" is missing)
 6  x  e           y   8  10   g   h     (new range because C changed to "y")
 7  x  f
 8  y  g
 9  y  h
10  y  i

SQL Server 2005. Спасибо.

Ответы [ 4 ]

4 голосов
/ 09 февраля 2012
DECLARE @myTable Table
(
    N INT,
    C CHAR(1),
    D CHAR(1)
)
INSERT INTO @myTable(N,C,D) VALUES(1,  'x', 'a');
INSERT INTO @myTable(N,C,D) VALUES(2,  'x', 'b');
INSERT INTO @myTable(N,C,D) VALUES(3,  'x', 'c');
INSERT INTO @myTable(N,C,D) VALUES(4,  'x', 'd');
INSERT INTO @myTable(N,C,D) VALUES(6,  'x', 'e');
INSERT INTO @myTable(N,C,D) VALUES(7,  'x', 'f');
INSERT INTO @myTable(N,C,D) VALUES(8,  'y', 'g');
INSERT INTO @myTable(N,C,D) VALUES(9,  'y', 'h');
INSERT INTO @myTable(N,C,D) VALUES(10, 'y', 'i');


WITH StartingPoints AS(

    SELECT A.*, ROW_NUMBER() OVER(ORDER BY A.N) AS rownum
    FROM @myTable AS A
    WHERE NOT EXISTS(
        SELECT *
        FROM @myTable B
        WHERE B.C = A.C
          AND B.N = A.N - 1
    )
 ),
 EndingPoints AS(
    SELECT A.*, ROW_NUMBER() OVER(ORDER BY A.N) AS rownum
    FROM @myTable AS A
    WHERE NOT EXISTS (
        SELECT *
        FROM @myTable B
        WHERE B.C = A.C
          AND B.N = A.N + 1
    )
 ) 
SELECT StartingPoints.C,
       StartingPoints.N AS [N1],
       EndingPoints.N AS [N2],
       StartingPoints.D AS [D1],
       EndingPoints.D AS [D2] 
FROM StartingPoints
JOIN EndingPoints ON StartingPoints.rownum = EndingPoints.rownum

Результаты :

C    N1          N2          D1   D2
---- ----------- ----------- ---- ----
x    1           4           a    d
x    6           7           e    f
y    8           10          g    i
1 голос
/ 10 февраля 2012

Используя этот ответ в качестве отправной точки, я получил следующее:

;
WITH data (N, C, D) AS (
  SELECT 1,  'x', 'a' UNION ALL
  SELECT 2,  'x', 'b' UNION ALL
  SELECT 3,  'x', 'c' UNION ALL
  SELECT 4,  'x', 'd' UNION ALL
  SELECT 6,  'x', 'e' UNION ALL
  SELECT 7,  'x', 'f' UNION ALL
  SELECT 8,  'y', 'g' UNION ALL
  SELECT 9,  'y', 'h' UNION ALL
  SELECT 10, 'y', 'i'
),
ranked AS (
  SELECT
    curr.*,
    Grp     = curr.N - ROW_NUMBER() OVER (PARTITION BY curr.C ORDER BY curr.N),
    IsStart = CASE WHEN pred.C IS NULL THEN 1 ELSE 0 END,
    IsEnd   = CASE WHEN succ.C IS NULL THEN 1 ELSE 0 END
  FROM data AS curr
    LEFT JOIN data AS pred ON curr.C = pred.C AND curr.N = pred.N + 1
    LEFT JOIN data AS succ ON curr.C = succ.C AND curr.N = succ.N - 1
)
SELECT
  C,
  N1 = MIN(N),
  N2 = MAX(N),
  D1 = MAX(CASE IsStart WHEN 1 THEN D END),
  D2 = MAX(CASE IsEnd   WHEN 1 THEN D END)
FROM ranked
WHERE 1 IN (IsStart, IsEnd)
GROUP BY C, Grp
1 голос
/ 10 февраля 2012

Функция RANK является более безопасной ставкой, чем ROW_NUMBER, в случае дублирования любых значений N, как в следующем примере:

declare @ncd table(N int, C char, D char);

insert into @ncd
select 1,'x','a' union all
select 2,'x','b' union all
select 3,'x','c' union all
select 4,'x','d' union all
select 4,'x','e' union all
select 7,'x','f' union all
select 8,'y','g' union all
select 9,'y','h' union all
select 10,'y','i' union all
select 10,'y','j';

with a as (
    select *
    , r = N-rank()over(partition by C order by N)
    from @ncd
)
select C=MIN(C)
, N1=MIN(N)
, N2=MAX(N)
, D1=MIN(D)
, D2=MAX(D)
from a
group by r;

Результат, который правильно выдерживает дубликаты 4 и 10:

C    N1          N2          D1   D2
---- ----------- ----------- ---- ----
x    1           4           a    e
x    7           7           f    f
y    8           10          g    j
0 голосов
/ 09 февраля 2012

Напишите хранимую процедуру.Он создаст и заполнит временную таблицу, которая будет содержать столбцы C, N1, N2, D1 и D2.

  • Создание временной таблицы
  • Использование курсора для зацикливания записей в таблицесодержащие N, C, D, упорядоченные по N
  • , используют переменную для определения нового диапазона (Ni
  • ВСТАВЬТЕ во временную таблицу для каждого обнаруженного диапазона (обнаружен новый диапазон или курсора)

Скажите, нужен ли вам пример кода.

...