Группа (иногда непоследовательный) период дней в Oracle SQL - PullRequest
2 голосов
/ 22 марта 2012

Я ищу запрос, который занимает в основном последовательные дни и группирует их в зависимости от того, являются ли они действительно последовательными, и от значения, относящегося к этой дате. Я использую Oracle версии 11g.

Это пример данных:

date          value
2012-01-01    2000
2012-01-02    2000 //(there is no data for Jan 03 for example)
2012-01-04    2000
2012-01-05    5000
2012-01-06    5000
2012-01-07    5000
2012-01-08    2000
2012-01-09    2000
2012-01-10    2000

(это результат довольно большого запроса)

То, что я ищу, сгруппировало бы эти дни в такие периоды:

from_date   to_date     value
2012-01-01  2012-01-02  2000
2012-01-04  2012-01-04  2000
2012-01-05  2012-01-07  5000
2012-01-08  2012-01-10  2000

Нам удалось сформулировать запрос, который делает то, что я хочу, но это не очень эффективный способ, и я уверен, что существует что-то лучшее / более элегантное. Вот что я сейчас использую:

with temp_table as (
select a.pk_date DATE1, c.pk_date DATE2, a.volume VOL1
from dm_2203 a, dm_2203 c
where a.volume = c.volume
  and a.pk_date <= c.pk_date
  and not exists (select 1 from dm_2203 b
                  where a.volume = b.volume
                    and a.pk_date = b.pk_date+1)
  and not exists (select 1 from dm_2203 d
                  where c.volume = d.volume
                    and c.pk_date = d.pk_date-1)  )
select * from temp_table y
where date2-date1+1 = (select count(*)
                       from dm_2203 z
                       where z.pk_date between y.date1 and y.date2
                         and y.vol1 = z.volume)
order by 1;

У кого-нибудь есть идеи, как сделать это быстрее и без всех объединений? Спасибо!

Ответы [ 2 ]

3 голосов
/ 22 марта 2012

Я думаю, это должно работать, а также быть достаточно эффективным (оно должно попасть в таблицу только один раз)

create table t ( d date, v number);

insert into t values (trunc(sysdate), 100);
insert into t values (trunc(sysdate+2), 100);
insert into t values (trunc(sysdate+3), 100);
insert into t values (trunc(sysdate+4), 100);
insert into t values (trunc(sysdate+5), 200);
insert into t values (trunc(sysdate+6), 200);
insert into t values (trunc(sysdate+7), 200);
insert into t values (trunc(sysdate+8), 100);

select min(d), max(d), v
from (
    select d, v, 
        sum( gc) over (partition by v order by d) g
    from (
        select d, v, 
            (case (d - lag(d) over ( partition by v order by d) )
                when 1 then 0
                else 1
             end) gc
        from t
    )
) group by v, g
order by min(d), v

Обратите внимание, что если вы хотите эффективно запустить логику для подмножества ваших данных, вы должны добавить предложение where во внутренний select.В противном случае у oracle возникнут проблемы с использованием любого индекса.

0 голосов
/ 22 марта 2012

Попробуйте:

with cte as (
select pk_date from_date, pk_date to_date, volume
from dm_2203 d
where not exists
(select null from dm_2203 e 
 where d.volume = e.volume and d.pk_date-1 = e.pk_date)
union all
select c.pk_date from_date, d.pk_date to_date, c.volume
from cte c
join dm_2203 d on d.volume = c.volume and d.pk_date = c.pk_date+1)
select from_date, max(to_date) to_date, volume "value"
from cte
group by from_date, volume
...