Skip to Main Content
  • Questions
  • How to Find Min/Max Dates for Data Having Constant Value

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, John.

Asked: November 27, 2014 - 11:21 am UTC

Last updated: December 01, 2014 - 1:42 am UTC

Version: 11.2.0

Viewed 10K+ times! This question is

You Asked

In the example below, how can I create a query to give me the results shown, such that for any sequence of consecutive rows, ordered by date, which have the same value for N, I can find the date values at the beginning and end of the range? I have a feeling it involves analytic functions but they hadn't been invented when I did my only SQL course in 1987!
Thanks in advance...
John

drop table jr_tmp;
create table jr_tmp (d date, n number);
insert into jr_tmp (d, n) values (date '2014-12-01', 1);
insert into jr_tmp (d, n) values (date '2014-12-02', 1);
insert into jr_tmp (d, n) values (date '2014-12-03', 1);
insert into jr_tmp (d, n) values (date '2014-12-04', 2);
insert into jr_tmp (d, n) values (date '2014-12-05', 2);
insert into jr_tmp (d, n) values (date '2014-12-06', 1);
insert into jr_tmp (d, n) values (date '2014-12-07', 1);
insert into jr_tmp (d, n) values (date '2014-12-08', 1);

<My query here>

D_FROM D_TO N
--------- --------- ----------
01-DEC-14 03-DEC-14 1
04-DEC-14 05-DEC-14 2
06-DEC-14 08-DEC-14 1

3 rows selected.

and Tom said...

there are various approaches, this is one I've used a lot:

ops$tkyte%ORA11GR2> select min(d), max(d), n, grp2
  2    from (
  3  select d, n, last_value(grp ignore nulls) over (order by d) grp2
  4    from (
  5  select d, n,
  6         case when (nvl(lag(n) over (order by d),n-1) <> n) then
  7                   row_number() over (order by d)
  8               end grp
  9    from jr_tmp
 10         )
 11         )
 12    group by n, grp2
 13   order by 1
 14  /

MIN(D)    MAX(D)             N       GRP2
--------- --------- ---------- ----------
01-DEC-14 03-DEC-14          1          1
04-DEC-14 05-DEC-14          2          4
06-DEC-14 08-DEC-14          1          6




starting from the inside, what we want to do is mark the beginning of each group:

ops$tkyte%ORA11GR2> select d, n,
  2         case when (nvl(lag(n) over (order by d),n-1) <> n) then
  3                   row_number() over (order by d)
  4               end grp
  5    from jr_tmp
  6  /

D                  N        GRP
--------- ---------- ----------
01-DEC-14          1          1
02-DEC-14          1
03-DEC-14          1
04-DEC-14          2          4
05-DEC-14          2
06-DEC-14          1          6
07-DEC-14          1
08-DEC-14          1

8 rows selected.


and then carry that down:

ops$tkyte%ORA11GR2> select d, n, last_value(grp ignore nulls) over (order by d) grp2
  2    from (
  3  select d, n,
  4         case when (nvl(lag(n) over (order by d),n-1) <> n) then
  5                   row_number() over (order by d)
  6               end grp
  7    from jr_tmp
  8         )
  9  /

D                  N       GRP2
--------- ---------- ----------
01-DEC-14          1          1
02-DEC-14          1          1
03-DEC-14          1          1
04-DEC-14          2          4
05-DEC-14          2          4
06-DEC-14          1          6
07-DEC-14          1          6
08-DEC-14          1          6

8 rows selected.




once we have that - aggregating is easy!


1987 is when I took my *first* SQL course. I keep taking them - it lets me do some pretty amazing things (just a hint, might be time for a refresher :) )

Rating

  (3 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

@John: If your refresher course includes 12c...

Stew Ashton, November 29, 2014 - 2:06 pm UTC


...you should learn about the MATCH_RECOGNIZE clause;
select * from jr_tmp
match_recognize(
  order by d
  measures first(d) d_from, last(d) d_to, n n
  pattern (a b*)
  define b as n = prev(n)
);

D_FROM     D_TO                N
---------- ---------- ----------
2014-12-01 2014-12-03          1 
2014-12-04 2014-12-05          2 
2014-12-06 2014-12-08          1

Tom Kyte
November 30, 2014 - 4:03 pm UTC

and I supposed for some more completeness, in 10g and above:

ops$tkyte%ORA11GR2> select min(d), max(d), n, grp
  2    from
  3  (
  4  select *
  5    from
  6  ( select d, n, grp, rn
  7      from jr_tmp
  8     model dimension by(row_number() over(order by d) rn)
  9     measures(d, n, 1 grp)
 10     rules(grp[rn=1] = 1,
 11           grp[rn > 1] =
 12             case when (n[cv() - 1] <> n[cv()])
 13                  then grp[cv()-1]+1
 14                  else grp[cv()-1]
 15              end
 16          )
 17  )
 18  )
 19  group by n, grp
 20  order by 1
 21  /

MIN(D)    MAX(D)             N        GRP
--------- --------- ---------- ----------
01-DEC-14 03-DEC-14          1          1
04-DEC-14 05-DEC-14          2          2
06-DEC-14 08-DEC-14          1          3



Throwing some gratitude your way...

John Rudman, November 30, 2014 - 11:18 pm UTC

Thanks Tom and Stew. Your solution looks eminently usable and has the advantage that the "table" is referred to only once. This is good, since in reality "jr_tmp" is a complex query itself containing an inline view.
I've heard of the Model clause and looking at Stew's example the syntax seems as arcane as the rumours suggest! (But then I guess so did subqueries in 1987!)
The customer is on 11.2.0 so the 12c solution will be stored in my locker till then.
Re: refresher course - I had to buy my own widescreen monitor, so the chances of them paying for a course are slim ("that's what the internet is for"). Nice thought though.
Tom Kyte
December 01, 2014 - 1:42 am UTC

hey hey - Oracle had subqueries from the get go! 1979... version 2.0 (the first version)

by 1987 - they were passé :)

Match_Recognize Example Doesn't Work

Lew, April 23, 2015 - 8:48 pm UTC

Trying to run the Match_Recognize example, I get:

pattern (a b*)
*
ERROR at line 5:
ORA-00905: missing keyword

A few of us are confused about "a" in the pattern, when there is no variable by that name in the query. Please explain/advise.

More to Explore

Analytics

Analytic SQL got you confused? Check out Connor McDonald's complete video course.