Skip to Main Content
  • Questions
  • Find agents with no transactions for 5 or more continuous days

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question.

Asked: May 03, 2017 - 10:45 am UTC

Last updated: May 10, 2017 - 12:12 pm UTC

Version: 11g Express Edition

Viewed 1000+ times

You Asked

Table1: TRANSACTION
FIELDS : AGENT_ID, TRNX_AMT, INSERTED_ON
create table TRANSACTION (AGENT_ID varchar2(10), TRN_AMT number(4,2),INSERTED_ON date);



Table2: AGENT_DETAILS
FIELDS: AGNET_ID,NAME,STATUS
create table AGENT_DETAILS(AGENT_ID varchar2(10), NAME varchar2(20),STATUS varchar2(10));

Data on TRANSACTION table
insert into TRANSACTION values( '1111',50.00,to_date('02-FEB-17'));
insert into TRANSACTION values( '1111',50.00,to_date('03-FEB-17'));
insert into TRANSACTION values( '2222',10.00,to_date('03-FEB-17'));
insert into TRANSACTION values( '2222',40.00,to_date('06-FEB-17'));
insert into TRANSACTION values( '1111',15.00,to_date('06-FEB-17'));
insert into TRANSACTION values( '2222',40.00,to_date('07-FEB-17'));
insert into TRANSACTION values( '2222',40.00,to_date('08-FEB-17'));
insert into TRANSACTION values( '2222',40.00,to_date('09-FEB-17'));
insert into TRANSACTION values( '2222',40.00,to_date('10-FEB-17'));
insert into TRANSACTION values( '2222',40.00,to_date('11-FEB-17'));
insert into TRANSACTION values( '2222',40.00,to_date('12-FEB-17'));
insert into TRANSACTION values( '1111',40.00,to_date('13-FEB-17'));
insert into TRANSACTION values( '2222',40.00,to_date('13-FEB-17'));
insert into TRANSACTION values( '1111',40.00,to_date('14-FEB-17'));
insert into TRANSACTION values( '1111',40.00,to_date('15-FEB-17'));
insert into TRANSACTION values( '1111',40.00,to_date('16-FEB-17'));
insert into TRANSACTION values( '1111',40.00,to_date('17-FEB-17'));
insert into TRANSACTION values( '1111',40.00,to_date('18-FEB-17'));
insert into TRANSACTION values( '1111',40.00,to_date('19-FEB-17'));
insert into TRANSACTION values( '2222',40.00,to_date('20-FEB-17'));
insert into TRANSACTION values( '1111',40.00,to_date('23-FEB-17'));
insert into TRANSACTION values( '1111',40.00,to_date('24-FEB-17'));
insert into TRANSACTION values( '1111',40.00,to_date('26-FEB-17'));
insert into TRANSACTION values( '1111',40.00,to_date('27-FEB-17'));
insert into TRANSACTION values( '2222',40.00,to_date('27-FEB-17'));



Data on Agent Table
insert into AGENT_DETAILS values ('1111','Test1','ACTIVE');
insert into AGENT_DETAILS values ('2222','Test2','ACTIVE');


Some history before i get to the problem, agent would be posting transaction each of which is updated on TRANSACTION table.

My problem is i need to identify the list of agent who have not performed any transaction for 5 continuous days in any given month.

Seeking help to build a query which would help identify the agent who have not performed transaction for 5 continuous days and give us this result

AGENT_ID, NO_OF_DAYS
1111,1
2222,2

For AGNET_ID - 1111 its 1 day - 12th Feb
For AGNET_ID - 2222 its 2 days - 19th and 26 Feb

and Chris said...

You can find the date of the previous transaction for an agent using lag(). So to find the number of days between two transactions subtract lag() from inserted_on:

inserted_on - lag(inserted_on) over (partition by agent_id order by inserted_on)


Then filter the result of this where this differences is >= 5. And group by agent_id and count:

with diffs as (
  select t.*, inserted_on - lag(inserted_on) over (partition by agent_id order by inserted_on) d
  from   transaction t
  order  by agent_id, inserted_on
)
  select agent_id, count(*) 
  from   diffs
  where  d >= 5
  group  by agent_id;

AGENT_ID  COUNT(*)  
1111      1         
2222      2  


PS: remember to use 4 digits years in dates!

Rating

  (5 ratings)

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

Comments

Srinath, May 05, 2017 - 7:37 am UTC

Thank you for the reply, but in case if the first transaction is done in the middle of the month the counts does not seem to match.

Fresh Data on TRANSACTION table

insert into TRANSACTION values( '1667',50.00,to_date('14-FEB-2017'));
insert into TRANSACTION values( '1667',50.00,to_date('17-FEB-2017'));

With the above data the lag query does not seem to fetch results.

I want the query to count the number of days after 5 continuous days, as days of absence.

For this Agnet_id from start of month to 13th its 8 days of absence and between 18th to 28th its 6 days

so the count of absence expected is 8+6=14 days...
Chris Saxon
May 05, 2017 - 8:58 am UTC

Please, be precise when asking questions! We can only go on what you give us...

If you're looking for all the days in a period an agent had no transactions, you can:

- Generate all the dates in the time period
- Cross join this with the agents, so you have a row for every agent on every day
- Then filter these where there not exists a transaction for that agent on that day

insert into AGENT_DETAILS values ('1667','Test3','ACTIVE');

with dates as (
  select date'2017-02-01'+level-1 dt
  from   dual
  connect by level <= 28
) 
  select agent_id, count(*)
  from   dates
  cross  join agent_details ad
  where  not exists (
    select * from transaction t
    where  dt = inserted_on
    and   t.agent_id = ad.agent_id
  )
  group  by agent_id; 

AGENT_ID  COUNT(*)  
1111      14        
2222      17        
1667      26 


From here, you can use the Tabibitosan method to find the consecutive days of inactivity. Group them together to get how many days there were in each stretch and limit to at least 5:

with dates as (
  select date'2017-02-01'+level-1 dt
  from   dual
  connect by level <= 28
), diffs as (
  select dt - row_number() over (partition by agent_id order by dt) grp, agent_id
  from   dates
  cross  join agent_details ad
  where  not exists (
    select * from transaction t
    where  dt = inserted_on
    and   t.agent_id = ad.agent_id
  )
)
  select agent_id, count(*) from diffs
  group  by agent_id, grp
  having count(*) >= 5;

AGENT_ID  COUNT(*)  
2222      6         
1111      6         
1667      13        
1667      11        
2222      6  

Srinath, May 05, 2017 - 8:39 am UTC

Further to my prev comment i have been able to make a bit of progress by changing the query as bellow

with diffs as (
select t.*, inserted_on - lag(inserted_on,1,20170201) over (partition by agent_id order by inserted_on) d
from transaction t
order by agent_id, inserted_on
)
select agent_id, sum(d-5)
from diffs
where d >= 5
group by agent_id;

but i am unable to fetch the count of days between 18th to 28th

To Srinath

Rajeshwaran, May 05, 2017 - 9:11 am UTC

Previously you said this to us.

.....
My problem is i need to identify the list of agent who have not performed any transaction for 5 continuous days in any given month.

Seeking help to build a query which would help identify the agent who have not performed transaction for 5 continuous days and give us this result
......


then you said this.


....
I want the query to count the number of days after 5 continuous days, as days of absence.
....


Those two looks very different to us.

for better clarity - here is the month of Feb 2017 Calender

demo@ORA12C> select *
  2  from (
  3  select x+level-1 dt ,
  4         to_char( x+level-1 ,'DY') dy,
  5         case when to_char( x+level-1 ,'DY') ='SUN'
  6            then to_number( to_char(x+level-1 ,'iw')) +1
  7            else to_number( to_char( x+level-1 ,'iw')) end iw
  8  from ( select to_date('01-feb-2017','dd-mon-yyyy') x from dual )
  9  connect by level <=28
 10       )
 11  pivot(
 12    max(dt)
 13    for dy in ('SUN','MON','TUE','WED','THU','FRI','SAT') )
 14  order by iw   ;

        IW 'SUN'       'MON'       'TUE'       'WED'       'THU'       'FRI'       'SAT'
---------- ----------- ----------- ----------- ----------- ----------- ----------- -----------
         5                                     01-FEB-2017 02-FEB-2017 03-FEB-2017 04-FEB-2017
         6 05-FEB-2017 06-FEB-2017 07-FEB-2017 08-FEB-2017 09-FEB-2017 10-FEB-2017 11-FEB-2017
         7 12-FEB-2017 13-FEB-2017 14-FEB-2017 15-FEB-2017 16-FEB-2017 17-FEB-2017 18-FEB-2017
         8 19-FEB-2017 20-FEB-2017 21-FEB-2017 22-FEB-2017 23-FEB-2017 24-FEB-2017 25-FEB-2017
         9 26-FEB-2017 27-FEB-2017 28-FEB-2017

demo@ORA12C>



....
For this Agnet_id from start of month to 13th its 8 days of absence
....


looking at the above calender from 01-feb to 13-feb it looks 9 working days (excluding week ends)


....
between 18th to 28th its 6 days
....


again 18-feb to 28-feb excluding week ends has 7 working days.

so start with this.

demo@ORA12C> select * from transaction;

AGENT_ID      TRN_AMT INSERTED_ON
---------- ---------- -----------
1667               50 14-FEB-2017
1667               50 17-FEB-2017

demo@ORA12C> select agent_id, inserted_on end_dt,
  2             lag(inserted_on) over( order by inserted_on) as start_dt,
  3             inserted_on - lag(inserted_on) over( order by inserted_on) as diff
  4  from transaction ;

AGENT_ID   END_DT      START_DT          DIFF
---------- ----------- ----------- ----------
1667       14-FEB-2017
1667       17-FEB-2017 14-FEB-2017          3

demo@ORA12C> select agent_id , ( select count(*)
  2             from dual
  3             where to_char( trunc(start_dt,'mm') + level -1 ,'DY') not in ('SAT','SUN')
  4             connect by level <= start_dt - trunc(start_dt,'mm')  ) begining_cnt,
  5      ( select count(*)
  6        from dual
  7        where to_char( end_dt+level-1 ,'DY') not in ('SAT','SUN')
  8        connect by level <= last_day(end_dt) - end_dt ) ending_cnt
  9  from (
 10  select agent_id, inserted_on end_dt,
 11             lag(inserted_on) over( order by inserted_on) as start_dt,
 12             inserted_on - lag(inserted_on) over( order by inserted_on) as diff
 13  from transaction
 14      )
 15  where diff <= 5     ;

AGENT_ID   BEGINING_CNT ENDING_CNT
---------- ------------ ----------
1667                  9          7

demo@ORA12C>


is this what you are looking for? the above code assumes just one "agent_id" in case of multiple "agent_id" have the necessary "partition by" clause in place.
Chris Saxon
May 05, 2017 - 1:53 pm UTC

Nice, but where did you get the "only weekdays" requirement from?

only weekdays requirements

Rajeshwaran, May 08, 2017 - 11:21 am UTC

but where did you get the "only weekdays" requirement from?

that was a safe assumption made from this given statement.

For this Agnet_id from start of month to 13th its 8 days of absence and between 18th to 28th its 6 days

between 18th to 28th we have around 11 days, excluding the weekends, left with only 5 days that was close to the given 6 days.

Chris Saxon
May 08, 2017 - 5:38 pm UTC

It doesn't look a safe assumption to me! There's 7 weekdays between 18th and 28th, not 6.

Srinath, May 10, 2017 - 3:54 am UTC

I was so much bogged down with this feature and when i finally landed at a place where i could get a solution i seem to have not explained my problem completely and precisely.

Let me try one more time. I have a transaction table into which every transaction done by the agent is logged. We have a penalty clause for an agent which goes like "In a given month, after every 5th day of continuous non transaction the agent would be penalized X amount per day" so it means if agent does not perform any transaction for 6 continuous days he should be penalized for 1 day. There is no distinction between weekdays and weekends all days are included for calculation. This counter for absence get reset at the start of every month.

So in case of agent 1667 a penalty has to be calculated as, from start of month to 13th its 8 days of absence and between 18th to 28th its 6 days

so the count of absence expected is 8+6=14 days...

I apologize if i might have wasted your time without providing clarity on the question, and i hope this follow up is clear...
Chris Saxon
May 10, 2017 - 12:12 pm UTC

Surely subtracting 5 from the counts in this solution gives what you need?

with dates as (
  select date'2017-02-01'+level-1 dt
  from   dual
  connect by level <= 28
), diffs as (
  select dt - row_number() over (partition by agent_id order by dt) grp, agent_id
  from   dates
  cross  join agent_details ad
  where  not exists (
    select * from transaction t
    where  dt = inserted_on
    and   t.agent_id = ad.agent_id
  )
)
  select agent_id, count(*) from diffs
  group  by agent_id, grp
  having count(*) >= 5;

More to Explore

Analytics

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