2014-01-09 3 views
0

Я провел некоторое время, имея дело со следующим:Определение границ N-группы

Представьте, что у вас есть N число групп с несколькими записями каждая и каждая запись имеет уникальныхstarting и ending точек ,

Другими словами:

ID|GroupName|StartingPoint|EndingPoint|seq(row_number)|desired_seq 
__|_________|_____________|___________|_______________|____________ 
1 | Grp1 |2014-01-06 |2014-01-07 |1    |1 
__|_________|_____________|___________|_______________|____________ 
2 | Grp1 |2014-01-07 | 2014-01-08|2    |2 
__|_________|_____________|___________|_______________|____________ 
3 | Grp2 |2014-01-08 | 2014-01-09|1    |1 
__|_________|_____________|___________|_______________|____________ 
4 | Grp1 |2014-01-09 | 2014-01-10|3    |1 
__|_________|_____________|___________|_______________|____________ 
5 | Grp2 |2014-01-10 | 2014-01-11|2    |1 
__|_________|_____________|___________|_______________|____________ 

Как вы можете видеть, starting point для каждой последовательной записи такой же, как ending point предыдущего.

В принципе, я хотел бы получить minimumS and maximumS для каждой группы на основе дат. Как только появится запись с новым именем группы, рассмотрите ее как новую группу и сбросьте последовательность.

Одно row_number() функция не достаточна для выполнения этой задачи, так как оно не отражают изменения в названиях групп. (Я включил колонку SEQ в данном образце, который представляет значения, генерируемые номер строки)

Желаемых результат на основании данных выборки:

1 Grp1 |2014-01-06 | 2014-01-08 
2 Grp2 |2014-01-08 | 2014-01-09 
3 Grp1 |2014-01-09 | 2014-01-10 
4 Grp2 |2014-01-10 | 2014-01-11 

То, что я пробовал:

;with cte as(
select * 
, row_number() over (partition by GroupName order by startingpoint) as seq 
from table1 
) 
select * 
into #temp2 
from cte t1 
left join cte t2 on t1.id=t2.id and t1.seq= t2.seq-1 

select * 
,(select startingPoint from #temp2 t2 where t1.id=t2.id and t2.seq= (select MIN(seq) from #temp2) as Oldest 
(select startingPoint from #temp2 t2 where t1.id=t2.id and t2.seq= (select MAX(seq) from #temp2) as MostRecent 
from #temp2 t1 
+0

Судя по таблице, похоже, что вы можете просто использовать 'MIN' и' MAX', если я что-то не упустил. – Zane

ответ

3

Это gaps-and-islands проблема с подгруппой. Трюк группируется по разности между двумя значениями ROW_NUMBER(), одним разделенным и одним безразделенным.

WITH t AS (
    SELECT 
    GroupName, 
    StartingPoint, 
    EndingPoint, 
    ROW_NUMBER() OVER(PARTITION BY GroupName ORDER BY StartingPoint) 
     - ROW_NUMBER() OVER(ORDER BY StartingPoint) AS SubGroupId 
    FROM #test 
) 
SELECT 
    ROW_NUMBER() OVER (ORDER BY MIN(StartingPoint)) AS SortOrderId, 
    GroupName          AS GroupName, 
    MIN(StartingPoint)        AS GroupStartingPoint, 
    MAX(EndingPoint)        AS GroupEndingPoint 
FROM t 
GROUP BY GroupName, SubGroupId 
ORDER BY SortOrderId 
0

Не уверен, но возможно:

SELECT DISTINCT 
    GroupName, 
    MIN(StartingPoint) OVER (PARTITION BY GroupName ORDER BY Id), 
    MAX(EndingPoint) OVER (PARTITION BY GroupName ORDER BY Id) 
FROM table1 

Поскольку partition не приводит к уменьшению количества строк будут первоначально дублируются записи, которые удаляются с distinct.

0

Это так намного проще с lag() функциональности в SQL Server 2012. То, как я подойти к этим проблемам, чтобы найти, где начать группы, назначая флаг 1 или 0 к каждой строке. Затем возьмите кумулятивную сумму 1 s, чтобы получить новый идентификатор группы.

В SQL Server 2008, вы можете сделать это с помощью коррелированных подзапросов (или соединения):

with table1_flag as (
     select t1.*, 
      isnull((select top 1 1 
        from table1 t2 
        where t2.groupname = t1.groupname and 
          t2.endingpoint = t1.startingpoint 
        ), 0) as groupstartflag 
     from table1 t1 
    ), 
    table1_flag_cum as (
     select tf.*, 
      (select sum(groupstartflag) 
       from table1_flag tf2 
       where tf2.groupname = tf.groupname and 
        tf2.startingpoint <= tf.startingpoint 
      ) as groupnum 
     from table1_flag tf 
    ) 
select groupnum, groupname, 
     min(startingpoint) as startingpoint, max(endingpoint) as endingpoint 
from table1_flag_cum 
group by groupnum, groupname; 
+0

Спасибо за помощь. Я протестировал запрос [SQLFiddle] (http://sqlfiddle.com/#!3/87a45/2), однако я не могу настроить его в соответствии с моими рекомендациями. Ваш запрос возвращает 07-10 для Grp1 и 08-11 для Grp2, что означает, что grps 2 включен в grp1 –

+0

@Kiril. , , Он включает 'groupname' в каждом сравнении, включая окончательную' group by'. Группы не должны мешать друг другу. –

+0

Ну. Это то, чего я ожидал бы, но я все еще получаю несколько групп, связанных с одним и тем же периодом. –

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