Awknewbie
Awknewbie

Reputation: 269

Group by Clause for specific records alone

I am planning to flatten my START END dates for customer as shown below.If the date ranges are continuous , I will merge those records . other wise i will keep it as such EG:

Input
Customer START END
A        2000  2001
A        2001  2007 
A        2009  2010
A        2011  2015

Expected Output
A        2000 2007
A        2009 2010
A        2011 2015

Using analytical function I was able to tag the records with continuous dates:

--TAG = 1 means continuous
select A *,
CASE WHEN  LEAD (START) OVER (PARTITION BY CUSTOMER ORDER BY START,END) = END
OR LAG (END_DT) OVER (PARTITION BY CUSTOMER ORDER BY START,END) = START
THEN 1 ELSE 0 END AS CONT_FLG
From TABLE CUSTOMER  

Customer START END   CONT_FLG
A        2000  2001  1
A        2001  2007  1
A        2009  2010  0
A        2011  2015  0

But I cannot proceed with min (START) and mAx (END) group by Customer as it will merge the non - continuos values also. Any good suggestions

Upvotes: 2

Views: 59

Answers (2)

Alex Poole
Alex Poole

Reputation: 191245

If you capture the actual lead/lag dates rather than a 0/1, then you get something like this:

select t.*,
  case when lag(end_dt) over (partition by customer order by start_dt)
    = start_dt then null else start_dt end as adj_start_dt,
  case when lead(start_dt) over (partition by customer order by start_dt)
    = end_dt then null else end_dt end as adj_end_dt
from t42 t
order by customer, start_dt;

CUSTOMER   START_DT     END_DT ADJ_START_DT ADJ_END_DT
-------- ---------- ---------- ------------ ----------
A              2000       2001         2000            
A              2001       2003                         
A              2003       2007                    2007 
A              2009       2010         2009       2010 
A              2011       2015         2011       2015 

I've split your second record into two adjacent records for effect, so that there is a row here with bot adjusted dates null. You can then remove those with both null, as those reflect records entirely inside a range, and you're left with the start and end dates of each period:

select *
from (
  select t.*,
    case when lag(end_dt) over (partition by customer order by start_dt)
      = start_dt then null else start_dt end as adj_start_dt,
    case when lead(start_dt) over (partition by customer order by start_dt)
      = end_dt then null else end_dt end as adj_end_dt
  from t42 t
)
where adj_start_dt is not null or adj_end_dt is not null
order by customer, start_dt;

CUSTOMER   START_DT     END_DT ADJ_START_DT ADJ_END_DT
-------- ---------- ---------- ------------ ----------
A              2000       2001         2000            
A              2003       2007                    2007 
A              2009       2010         2009       2010 
A              2011       2015         2011       2015 

And you can then collapse those with nulls, as the adjacent rows (with lead/lag) are now related:

select distinct customer,
  case when adj_start_dt is null then
    lag(adj_start_dt) over (partition by customer order by start_dt)
    else adj_start_dt end as grp_start_dt,
  case when adj_end_dt is null then
    lead(adj_end_dt) over (partition by customer order by start_dt)
    else adj_end_dt end as grp_end_dt
from (
  select t.*,
    case when lag(end_dt) over (partition by customer order by start_dt)
      = start_dt then null else start_dt end as adj_start_dt,
    case when lead(start_dt) over (partition by customer order by start_dt)
      = end_dt then null else end_dt end as adj_end_dt
  from t42 t
)
where adj_start_dt is not null or adj_end_dt is not null
order by customer, grp_start_dt;

CUSTOMER GRP_START_DT GRP_END_DT
-------- ------------ ----------
A                2000       2007 
A                2009       2010 
A                2011       2015 

SQL Fiddle demo.

Upvotes: 1

Tony Andrews
Tony Andrews

Reputation: 132570

Try something like

select customer, min(start), min(end)
from
(
select A *,
CASE WHEN  LEAD (START) OVER (PARTITION BY CUSTOMER ORDER BY START,END) = END
OR LAG (END_DT) OVER (PARTITION BY CUSTOMER ORDER BY START,END) = START
THEN 1 ELSE 0 END AS CONT_FLG
From TABLE CUSTOMER  
) 
group by customer, cont_flg, case when cont_flg=0 then start end

Upvotes: 1

Related Questions