2012-03-22 4 views
2

Я ищу запрос, который берет в основном последовательные дни и группирует их вместе в зависимости от того, действительно ли они последовательно и по значению, принадлежащему этой дате. Я использую Oracle версии 11g.Группа a (иногда не последовательный) период дней в Oracle SQL

Это несколько примеров данных:

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; 

Кто-нибудь есть идеи о том, как сделать это быстрее и без всех стыков? Благодаря!

+0

Какая версия Oracle это? –

+1

Я не понимаю логику для вашего желаемого вывода, почему эти две строки выводят? – Lamak

+0

@Lamak: набор из двух строк не является желаемым выходом - набор из четырех строк является желаемым выходом. –

ответ

3

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

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 во внутреннем большинстве. В противном случае оракул будет иметь проблемы с использованием любого индекса.

+3

Очень приятно. Вот [SQL Fiddle] (http://sqlfiddle.com/#!4/3ecee/6), используя тестовые данные и структуру таблицы OP, используя код –

+0

thanx для ссылки на SQL Fiddle. Удивительный инструмент. –

+0

@JensSchauder: Это выглядит очень хорошо, и я пытаюсь адаптировать его сейчас для моего запроса. У меня на самом деле есть еще несколько полей, чем только дата и значение, но общая идея должна быть одинаковой. Спасибо, уже! – Xuntar

0

Try:

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