Cześć Mam dane codziennie poniżej:

daytime        value
01.01.2017     20000
02.01.2017     20000
03.01.2017     20000
04.01.2017     35000
05.01.2017     35000
06.01.2017     40000
07.01.2017     40000
08.01.2017     50000

Jak mogę mieć w formacie zasięgu datego, takiego jak poniżej?

FromDate     ToDate      Value
01.01.2017   03.01.2017  20000
04.01.2017   05.01.2017  35000
06.01.2017   07.01.2017  40000
08.01.2017   08.01.2017  50000

Dzięki!

-1
akira 16 luty 2017, 14:25

2 odpowiedzi

Najlepsza odpowiedź

Tabibitosan Umożliwia to bardzo łatwo:

WITH your_table AS (SELECT to_date('01/01/2017', 'dd/mm/yyyy') daytime, 20000 VALUE FROM dual UNION ALL
                    SELECT to_date('02/01/2017', 'dd/mm/yyyy') daytime, 20000 VALUE FROM dual UNION ALL
                    SELECT to_date('03/01/2017', 'dd/mm/yyyy') daytime, 20000 VALUE FROM dual UNION ALL
                    SELECT to_date('04/01/2017', 'dd/mm/yyyy') daytime, 35000 VALUE FROM dual UNION ALL
                    SELECT to_date('05/01/2017', 'dd/mm/yyyy') daytime, 35000 VALUE FROM dual UNION ALL
                    SELECT to_date('06/01/2017', 'dd/mm/yyyy') daytime, 40000 VALUE FROM dual UNION ALL
                    SELECT to_date('07/01/2017', 'dd/mm/yyyy') daytime, 40000 VALUE FROM dual UNION ALL
                    SELECT to_date('08/01/2017', 'dd/mm/yyyy') daytime, 50000 VALUE FROM dual UNION ALL
                    SELECT to_date('09/01/2017', 'dd/mm/yyyy') daytime, 20000 VALUE FROM dual)
-- end of mimicking your table with data in it. See SQL below:
SELECT MIN(daytime) fromdate,
       MAX(daytime) todate,
       VALUE
FROM   (SELECT daytime,
               VALUE,
               row_number() OVER (ORDER BY daytime) - row_number() OVER (PARTITION BY VALUE ORDER BY daytime) grp
        FROM   your_table)
GROUP BY grp,
         VALUE
ORDER BY MIN(daytime);

FROMDATE   TODATE          VALUE
---------- ---------- ----------
01/01/2017 03/01/2017      20000
04/01/2017 05/01/2017      35000
06/01/2017 07/01/2017      40000
08/01/2017 08/01/2017      50000
09/01/2017 09/01/2017      20000

Co to jest porównanie numeru wiersza dla wszystkich zamówionych wierszy według daty, a następnie numer wiersza dla wszystkich wierszy dla każdej wartości zamówionej według daty. Jeśli wiersze wartości są kolejne w głównym zestawie danych, to różnica między dwoma zestawami danych pozostaje taka sama, dzięki czemu możesz wtedy grupa. Jeśli jest luka, różnica wzrasta.

W powyższym przykładzie pierwsze trzy wiersze dla wartości = 20000 występują pierwsze trzy rzędy całego zestawu, więc różnica będzie wynosić 0. Jednak czwarta wartość = 20000 rzędu jest 9 rzędem w całym zestawie, więc Różnica wynosi teraz 5. Możesz łatwo zobaczyć, że wartość 20000 spada na dwie grupy, a jako taką można znaleźć min / max dla każdej grupy oddzielnie, w tym obliczanie tej różnicy w grupie według klauzuli.

N.B. Założym to, że daty w danych są kolejne lub że jeśli brakuje dat, które zakładasz, że wartość pozostaje taka sama dla brakujących dat. Jeśli masz brakujące dni i chcesz, aby wartości na luki, aby pokazać w różnych grupach, trzeba było dołączyć do podłączenia do podzewności zawierającej brakujące daty. W takim przypadku myślę, że odpowiedź Gurva (z dodatkową klauzulą w stwierdzeniu przypadku, o którym wspomniałem w komentarzach) byłoby najlepszym użyciem, ponieważ uniknąłoby potrzeby dołączenia zewnętrznego do listy kolejnych dat.

3
Boneist 16 luty 2017, 14:02

Jeśli poprawnie rozumiem, chcesz grupować tylko wtedy, gdy są takie same dla kolejnych dat.

Możesz użyć funkcji okien, aby wygenerować grupy oparte na wartości i zwiększając kolejność daty, a następnie znajdź wymagane agregaty.

with your_table(daytime      ,value) as (
    select to_date('13.02.2017','dd.mm.yyyy'),25000 from dual union all
    select to_date('14.02.2017','dd.mm.yyyy'),20000 from dual union all
    select to_date('15.01.2017','dd.mm.yyyy'),90000 from dual union all
    select to_date('16.01.2017','dd.mm.yyyy'),90000 from dual union all
    select to_date('17.01.2017','dd.mm.yyyy'),95800 from dual union all
    select to_date('18.01.2017','dd.mm.yyyy'),95800 from dual union all
    select to_date('19.01.2017','dd.mm.yyyy'),95800 from dual union all
    select to_date('20.01.2017','dd.mm.yyyy'),95800 from dual union all
    select to_date('21.01.2017','dd.mm.yyyy'),95800 from dual union all
    select to_date('22.01.2017','dd.mm.yyyy'),95800 from dual union all
    select to_date('23.01.2017','dd.mm.yyyy'),95800 from dual union all
    select to_date('24.01.2017','dd.mm.yyyy'),90000 from dual union all
    select to_date('25.01.2017','dd.mm.yyyy'),90000 from dual union all
    select to_date('26.01.2017','dd.mm.yyyy'),90000 from dual 
)
select
    min(daytime) fromdate,
    max(daytime) todate,
    value
from (
    select
        t.*,
        sum(x) over (order by daytime) grp
    from (
        select 
            t.*,
            case when value = lag(value) over (order by daytime) 
            then 0 else 1 end x
        from your_table t
    ) t
) t group by grp, value
order by fromdate;

Produkuje:

FROMDATE    TODATE      VALUE
15-JAN-17   16-JAN-17   90000
17-JAN-17   23-JAN-17   95800
24-JAN-17   26-JAN-17   90000
13-FEB-17   13-FEB-17   25000
14-FEB-17   14-FEB-17   20000
1
Gurwinder Singh 16 luty 2017, 12:53