2015-03-15 2 views
4

окружающей среды является SQL Server 2014.Объединить смежные, пересекающиеся и встроенные диапазоны в взаимоисключающие диапазоны

Я имею дело с сокращением многих деталей страхового охвата (небольшие диапазонов первый и последний) в гораздо большем взаимоисключающий (ME) диапазоны непрерывной регистрации.

Для большей ясности проблема сводится к выборке данных, отсортированных по id, во-первых, последним. F (n) и L (n) - это первое и последнее значения в записи n внутри id.

Большинство диапазонов подробно типичные

  • смежный, F (п) = L (п-1) + 1

Но есть дьявол в деталях - добро пожаловать в реальном мире информации ,

  • соединены несмежных, Р (п) < = L (п-1)
    • встроен L (п) < = L (п-1)
    • перекрытия L (п) > L (п-1)
  • отсоединен несмежные
    • зазора определяет границы консолидированных диапазонов, которые являются взаимоисключающими
    • ME (я) .last = макс преждепользования L

Эта фотография показывает, в большинстве случаев

Have 
    1  30  60  90  120 
    +-------+--------+--------+--------+ 
1 +-------+        (1:30) 
2   +-------+     (31:60) adjacent 
3    +--+      (40:50) embedded 
4     +     (61:61) adjacent some earlier 
5     +-+     (61:65) adjacent some earlier 
6     +--+    (61:75) adjacent some earlier 
7      +--+    (65:80) overlap 
8       +---------+ (85:120) gap, boundaries of ME ranges located 
9       +-------+ (91:120) 
10        +--+ (110:120) 

Want 

    1  30  60  90  120 
    +-------+--------+--------+--------+ 
1 +----------------------+    (1:80) 
2       +---------+ (85:120) 

There are other unusual cases, such as embed followed by gap 

..... 
    .. 
     .... 
AAAAA BBBB 


DROP TABLE #Details 
CREATE TABLE #Details (id int, first int, last int); 

insert into #Details values (1, 1, 30); 
insert into #Details values (1, 31, 60); 
insert into #Details values (1, 40, 50); 
insert into #Details values (1, 61, 75); 
insert into #Details values (1, 65, 80); 
insert into #Details values (1, 85, 120); 
insert into #Details values (1, 91, 120); 
insert into #Details values (1, 110, 120); 

Я прочитал некоторые ответы на стек и Refactoring Ranges, но не может сделать прыжок к моему размещение данных.

--Для jpw--

Типичный анализ может включать в себя 20000 идентификаторов с 200 подробных записей. Эти случаи были обработаны путем загрузки на локальный компьютер и обработаны (как курсором) на шаге SAS Data. В худшем случае - порядка 650 тыс. Идентификаторов и 150 млн. Деталей - слишком много данных для загрузки и приводит к другим проблемам с ресурсами. Я считаю, что все детали могут находиться в диапазоне строк 1.2B. Несмотря на это, если все это можно сделать на SQL-сервере, весь процесс упрощается.

+0

Примерно сколько строк есть в таблице? Сотни, тысячи, миллионы и более? – jpw

+0

Типичный анализ может включать 20 000 идентификаторов с 200 подробными записями. – Richard

+0

Я пробовал свой подход к большему набору данных, и оказалось, что он вообще не работает ... подумает об этом немного подробнее. – jpw

ответ

1

Все права на этот ответ помогут вам подойти. Чувствует себя немного испеченным, но определенно на правильном пути. Я уверен, что вы можете адаптировать это к вашим потребностям. Суть проблемы заключается в создании семейств перекрытий. Я использовал рекурсивный cte после создания родительского списка. Подробнее см. Мои объяснения ниже.

Исходные данные

USERID  RangeStart RangeEnd 
----------- ----------- ----------- 
1   1   2 
1   2   4 
1   3   5 
1   6   7 
2   1   3 
2   5   9 
2   11   14 
2   14   15 

Запрос

DECLARE @USERID TABLE (USERID INT, RangeStart INT, RangeEnd INT) 
INSERT INTO @USERID (USERID, RangeStart,RangeEnd) VALUES 
(1,1,2),(1,2,4),(1,3,5),(1,6,7), 
(2,1,3),(2,5,9),(2,11,14),(2,14,15) 

;WITH Data AS (
    SELECT ROW_NUMBER() OVER (ORDER BY USERID, RangeStart) AS MasterOrdering, 
      USERID, 
      RangeStart, 
      RangeEnd, 
      LAG(RangeStart) OVER (PARTITION BY USERID ORDER BY RangeStart ASC) AS PreviousStart, 
      LAG(RangeEnd) OVER (PARTITION BY USERID ORDER BY RangeStart ASC) AS PreviousEnd 
    FROM @USERID 
), ParentChild AS (
    SELECT *, 
      Parent = CASE 
          WHEN PreviousStart IS NULL AND PreviousEnd IS NULL THEN MasterOrdering 
          WHEN PreviousEnd NOT BETWEEN RangeStart AND RangeEnd THEN MasterOrdering 
          ELSE 0 
         END 
    FROM Data 
), Family AS (
    SELECT MasterOrdering, 
      USERID, 
      RangeStart, 
      RangeEnd, 
      PreviousStart, 
      PreviousEnd, 
      Parent 
    FROM ParentChild 
    WHERE Parent > 0 
    UNION ALL 
    SELECT A.MasterOrdering, 
      A.USERID, 
      A.RangeStart, 
      A.RangeEnd, 
      A.PreviousStart, 
      A.PreviousEnd, 
      F.Parent 
    FROM ParentChild AS A 
      INNER JOIN Family AS F ON (A.MasterOrdering = F.MasterOrdering + 1 
             AND A.parent = 0) 
) 
SELECT USERID, 
     MIN(RangeStart) AS RangeStart, 
     MAX(RangeEnd) AS RangeEnd, 
     MIN(MasterOrdering) AS MasterOrdering 
FROM Family 
GROUP BY UserID,Parent 
ORDER BY MIN(MasterOrdering) 

Результаты

USERID  RangeStart RangeEnd MasterOrdering 
----------- ----------- ----------- -------------------- 
1   1   5   1 
1   6   7   4 
2   1   3   5 
2   5   9   6 
2   11   15   7 

Query Walk Through

Предположения

  • SQL Server 2014
  • Разумное понимание функций окна
  • Фирма схватывание CTE-х и Рекурсивный CTE в частности.

Шаг за шагом

  • начинается, запрашивая данные. Это использует функцию ROW_NUMBER, чтобы установить последовательный ЗАКАЗ, основанный на USERID и Ascending RangeStarts. Его использовали позже, чтобы упорядочить список в этом порядке. Функции LAG извлекают предыдущие строки PreviousStart и PreviousEnd. Это также используется при установлении родителей, а число используется как идентификатор семьи на основе этого родительского идентификатора.
  • ParentChild заполняет родительскую колонку на основе 2 правил. Если значения previousstart и previousend равны NULL, это означает, что в partion они являются первым элементом. Мы назначаем их автоматически как родительскую строку. Тогда, если PreviousEnd не находится между начальным и конечным диапазоном, мы также используем это как родительский.
  • Семья - это настоящая магия. Используя рекурсивный CTE, мы запрашиваем для всех родителей, а затем объединяем всех не-родителей с их связанным с ним общим порядком + 1. Плюс + 1 гарантирует, что мы заполняем все значения 0 и предикат A.parent = 0, гарантируя, что мы присоединяемся к не назначенным членов семьи в родительский диапазон.
  • В конечном итоге у нас просто есть группа min и max по идентификатору пользователя и родительскому столбцу (который теперь является уникальным номером на одно перекрывающееся семейство).

Посмотрите. Отличный вопрос и немного забавный тизер.

Приветствия

Matt

Смежные вопросы