Как избежать наложения диапазонов дат при использовании предложения группировки? - PullRequest
4 голосов
/ 07 января 2011

У меня есть ситуация, когда мне нужно найти промежутки времени между изменениями значений.Я попробовал простую группу с предложением, но это устраняет дублирующие изменения.Рассмотрим следующий пример:

create table #items (
      code varchar(4)
    , class varchar(4)
    , txdate datetime
)

insert into #items (code, class, txdate) values ('A', 'C', '2010-01-01');
insert into #items (code, class, txdate) values ('A', 'C', '2010-01-02');
insert into #items (code, class, txdate) values ('A', 'C', '2010-01-03');
insert into #items (code, class, txdate) values ('A', 'D', '2010-01-04');
insert into #items (code, class, txdate) values ('A', 'D', '2010-01-05');
insert into #items (code, class, txdate) values ('A', 'C', '2010-01-06');
insert into #items (code, class, txdate) values ('A', 'C', '2010-01-07');
insert into #items (code, class, txdate) values ('A', 'D', '2010-01-08');
insert into #items (code, class, txdate) values ('A', 'D', '2010-01-09');

select code
, class
, min(txdate) mindate
, max(txdate) maxdate
from #items
group by code, class

Это возвращает следующие результаты (обратите внимание на перекрывающиеся диапазоны дат):

|code|class|mindate   |maxdate   |
----------------------------------
|A   |C    |2010-01-01|2010-01-07|
|A   |D    |2010-01-04|2010-01-09|

Я бы хотел, чтобы запрос возвратил следующее:

|code|class|mindate   |maxdate   |
----------------------------------
|A   |C    |2010-01-01|2010-01-03|
|A   |D    |2010-01-04|2010-01-05|
|A   |C    |2010-01-06|2010-01-07|
|A   |D    |2010-01-08|2010-01-09|

Есть идеи и предложения?

Ответы [ 4 ]

2 голосов
/ 07 января 2011

РЕДАКТИРОВАТЬ : Как указано в комментариях, это все еще не совсем верно.

;with cteNtile as (
    select code, class, txdate, 
           ntile((select count(*) from (select NULL as dummy from #items group by code, class) a)) over(partition by code, class order by txdate) as tilenum
        from #items
)
select code, class, MIN(txdate) as mindate, MAX(txdate) as maxdate
    from cteNtile
    group by code, class, tilenum
    order by mindate, maxdate
2 голосов
/ 07 января 2011

Вот запрос, который может дать вам желаемый результат.

;WITH items1 AS (
SELECT ROW_NUMBER() OVER (ORDER BY txdate) rowid, code, class, txdate
from #items
),
items2 AS (
SELECT ROW_NUMBER() OVER (ORDER BY rowid) id, rowid, i1.Code, i1.Class, i1.txdate
FROM items1 i1
WHERE NOT EXISTS (SELECT 1 FROM items1 i2
                  WHERE i2.txdate < i1.txdate
                  AND i2.class = i1.class
                  AND i2.Code = i1.Code 
                  AND i2.rowid+1=i1.rowid)
)
SELECT items2.code, items2.class, items2.txdate mindate, items1.txdate maxdate
FROM items2,  items2 items3, items1 
WHERE (items2.id+1=items3.id AND items3.rowid-1=items1.rowid)
OR items2.rowid = (SELECT MAX(t.rowid) FROM items1 t)
UNION 
SELECT items2.code, items2.class, MAX(items2.txdate) mindate, MAX(items1.txdate) maxdate
FROM items2, items1 
WHERE items1.class = items2.class
GROUP BY items1.class, items2.class, items2.code, items2.class
ORDER BY items2.txdate
0 голосов
/ 07 января 2011

После исследования SQL SERVER ISLANDS в соответствии с предложением @KM, я пришел к следующему запросу, который, кажется, работает хорошо, когда в набор данных добавляются дополнительные коды классов.

select a.code, a.class, a.txdate as mindate, b.txdate as maxdate
from (
    --Find minimum island
    select code
        , class
        , txdate
        , row_number() over (order by code, class, txdate) as n
    from #items tb1
    where not exists (
        select *
        from #items tb2
        where datediff(d, tb1.txdate, tb2.txdate) = -1      
          and tb1.class = tb2.class
          and tb1.code = tb2.code
    ) 
) as a
inner join (
    --Find maximum island 
    select code
        , class
        , txdate
        , row_number() over (order by code, class, txdate) as n
    from #items tb1
    where not exists (
        select *
        from #items tb2
        where datediff(d, tb1.txdate, tb2.txdate) = 1   
          and tb1.class = tb2.class
          and tb1.code = tb2.code
    ) 
) as b on a.n = b.n

Единственный недостаток этого подхода заключается в том, что количество записей в минимальном наборе должно соответствовать количеству записей в максимальном наборе.До сих пор я не смог сделать ничего, что могло бы сделать это неправдой.Однако я не тестировал нулевые значения или производительность.

0 голосов
/ 07 января 2011

Я думаю, что вы не можете сделать это с помощью простого оператора select.

Вы можете использовать курсор, чтобы перебирать строки и идентифицировать "классовые" изменения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...