I have a table with the following structure: ID, Month, Year, Value with values for one entry per id per month, most months have the same value.
I would like to crea
I couldn't get the response from ngz to work when the input table contains multiple ids and date ranges that span years. I have a solution that does work, but with qualifications. It will only give you the correct answers if you know that you have a row for every month/year/id combination within the range. If there are "holes" it won't work. If you have holes, I know of know good way to do it other than writing some PL/SQL and using a cursor loop to create a new table in the format you want.
By the way, this is why data modeled this way is an abomination. You should always store stuff as start/from range records, not as discrete time period records. It's trivial to transform the former into the latter with a "multiplier" table, but it's almost impossible (as you've seen) to go the other direction.
SELECT ID
, VALUE
, start_date
, end_date
FROM (SELECT ID
, VALUE
, start_date
, CASE
WHEN is_last = 0
THEN LEAD(end_date) OVER(PARTITION BY ID ORDER BY start_date)
ELSE end_date
END end_date
, is_first
FROM (SELECT ID
, VALUE
, TO_CHAR(the_date, 'YYYY.MM') start_date
, TO_CHAR(NVL(LEAD(the_date - 31) OVER(PARTITION BY ID ORDER BY YEAR
, MONTH), the_date), 'YYYY.MM') end_date
, is_first
, is_last
FROM (SELECT ID
, YEAR
, MONTH
, TO_DATE(TO_CHAR(YEAR) || '.' || TO_CHAR(MONTH) || '.' || '15', 'YYYY.MM.DD') the_date
, VALUE
, ABS(SIGN(VALUE -(NVL(LAG(VALUE) OVER(PARTITION BY ID ORDER BY YEAR
, MONTH), VALUE - 1)))) is_first
, ABS(SIGN(VALUE -(NVL(LEAD(VALUE) OVER(PARTITION BY ID ORDER BY YEAR
, MONTH), VALUE - 1)))) is_last
FROM test_table)
WHERE is_first = 1
OR is_last = 1))
WHERE is_first = 1