Skip to Main Content

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Vigneshpandi.

Asked: February 15, 2016 - 8:08 am UTC

Last updated: February 15, 2016 - 9:53 am UTC

Version: 11g

Viewed 1000+ times

You Asked

Hello all,

I need a query for the following.

NAME START_YEAR END_YEAR
-------------------- ---------- ----------
swift 2007 2009
swift 2009 2011
swift 2011 2013
maruti 2009 2011
maruti 2013 2015

I have a table like above, the table name is "CARS"

and i need a output like,

NAME START_YEAR END_YEAR
-------------------- ---------- ----------
swift 2007 2013
maruti 2009 2011
maruti 2013 2015

Kindly update me ASAP.

Regards,
Vignesh
ATC, India Development

and Chris said...

I'm guessing you want a query that splits the rows in groups of consecutive ranges by name? i.e. find if there's any missing rows where the previous end_year <> current start_year?

If so, there's a couple of methods. If the gap between start and end years is always two you could use the Tabibitosan method:

http://rwijk.blogspot.co.uk/2014/01/tabibitosan.html

select name, min(start_year) mn, max(end_year) mx from (
  select c.*, 
         start_year - (
            row_number() over (partition by name order by start_year) * 2
         ) rn 
  from   cars c
) 
group  by name, rn;

NAME               MN         MX
---------- ---------- ----------
swift            2007       2013
maruti           2009       2011
maruti           2013       2015


For this to work either:

- There must always be 2 years between start and end or
- You can find a function that calculates the different between consecutive rows to 1 (and > 1 if they're not consecutive).

If you can't meet these criteria, you need to check whether the difference between the current start year = previous end. You can do this using lag(). If it is, then assign a group/row number:

case 
  when lag(end_year, 1, -1) over (partition by name order by end_year) != start_year then 
    row_number() over (order by end_year)
end rn


You can then "fill down" this value, include it in your group by to get the min/max consecutive years for each make. e.g.:

select name, min(start_year) mn, max(end_year) mx from (
  select name, start_year, end_year, rn, 
         last_value(rn) ignore nulls over (
           partition by name order by start_year
         ) lv
  from   (
    select case 
              when lag(end_year, 1, -1) over (partition by name order by end_year) != start_year then 
                row_number() over (order by end_year)
           end rn, 
           c.*
    from   cars c
  )
)
group  by name, lv;

NAME               MN         MX
---------- ---------- ----------
swift            2007       2013
maruti           2009       2011
maruti           2013       2015


Rob also discusses this approach in more detail in the Tabibitosan link above.

Rating

  (2 ratings)

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

Comments

Thanking

Vigneshpandi Marimuthu, February 15, 2016 - 9:59 am UTC

Thankyou

SQL Pattern matching

Rajeshwaran, Jeyabal, February 16, 2016 - 12:53 pm UTC

With 12c Pattern matching in place, this would be like this.

rajesh@ORA12C> set feedback off
rajesh@ORA12C> drop table t purge;
rajesh@ORA12C> create table t(x varchar2(20),y int,z int);
rajesh@ORA12C> insert into t values('swift',2007,2009 );
rajesh@ORA12C> insert into t values('swift',2009,2011 );
rajesh@ORA12C> insert into t values('swift',2011,2013 );
rajesh@ORA12C> insert into t values('maruti',2009,2011);
rajesh@ORA12C> insert into t values('maruti',2013,2015);
rajesh@ORA12C> commit;
rajesh@ORA12C> set feedback on
rajesh@ORA12C>
rajesh@ORA12C> column name format a10
rajesh@ORA12C> select *
  2  from t
  3  match_recognize(
  4    order by x,y
  5    measures
  6      x as name,
  7      b1.y as start_yr,
  8      nvl( last(b2.z),b1.z) as end_yr,
  9      match_number() as mno
 10    one row per match
 11    pattern(b1 b2*)
 12    define
 13      b2 as x = prev(x)  and
 14             y = prev(z) )
 15  order by 1
 16  /

NAME         START_YR     END_YR        MNO
---------- ---------- ---------- ----------
maruti           2009       2011          1
maruti           2013       2015          2
swift            2007       2013          3

3 rows selected.

rajesh@ORA12C>

More to Explore

Analytics

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