Skip to Main Content
  • Questions
  • How to aggregate records by month year but based on starting date 26th and ending date 25th for each month not based upon calendar months?

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Khamis .

Asked: July 31, 2024 - 11:35 am UTC

Last updated: August 05, 2024 - 6:55 am UTC

Version: Oracle Database 12c Release 12.2.0.1.0 - 64bit

Viewed 100+ times

You Asked

How to aggregate records by month year but based on starting date 26th and ending date 25th for each month not based upon calendar months?
For example, I have table:

Order_no Customer_id Date Amount
1001 201 2024-06-11 20:10:00 2000
1002 201 2024-06-25 15:35:00 2100
1003 201 2024-06-26 10:20:00 1700
1004 201 2024-07-15 20:58:00 800
1005 201 2024-07-25 13:45:00 1500
1006 201 2024-07-29 16:25:00 900

I want to produce a year and month aggregation of the sum of the amounts, but periods have a specific start date and end date, for example the start date is 26th and end date 25th of each month.

The result I want:

Customer_id Month Sum_Amount
201 Jun-24 4100
201 Jul-24 4000
201 Aug-24 900

Thanks in advance.

and Connor said...

Something like this perhaps

SQL> with t as
  2   ( select date '2024-05-01' + rownum - 1  created from dual
  3     connect by level <= 100 )
  4  select
  5    created,
  6    trunc(created-23,'MM') as adjusted_mth
  7  from t
  8  order by 1;

CREATED   ADJUSTED_
--------- ---------
01-MAY-24 01-APR-24
02-MAY-24 01-APR-24
03-MAY-24 01-APR-24
04-MAY-24 01-APR-24
05-MAY-24 01-APR-24
06-MAY-24 01-APR-24
07-MAY-24 01-APR-24
08-MAY-24 01-APR-24
09-MAY-24 01-APR-24
10-MAY-24 01-APR-24
11-MAY-24 01-APR-24
12-MAY-24 01-APR-24
13-MAY-24 01-APR-24
14-MAY-24 01-APR-24
15-MAY-24 01-APR-24
16-MAY-24 01-APR-24
17-MAY-24 01-APR-24
18-MAY-24 01-APR-24
19-MAY-24 01-APR-24
20-MAY-24 01-APR-24
21-MAY-24 01-APR-24
22-MAY-24 01-APR-24
23-MAY-24 01-APR-24
24-MAY-24 01-MAY-24
25-MAY-24 01-MAY-24
26-MAY-24 01-MAY-24
27-MAY-24 01-MAY-24
28-MAY-24 01-MAY-24
29-MAY-24 01-MAY-24
30-MAY-24 01-MAY-24
31-MAY-24 01-MAY-24
01-JUN-24 01-MAY-24
02-JUN-24 01-MAY-24
03-JUN-24 01-MAY-24
04-JUN-24 01-MAY-24
05-JUN-24 01-MAY-24
06-JUN-24 01-MAY-24
07-JUN-24 01-MAY-24
08-JUN-24 01-MAY-24
09-JUN-24 01-MAY-24
10-JUN-24 01-MAY-24
11-JUN-24 01-MAY-24
12-JUN-24 01-MAY-24
13-JUN-24 01-MAY-24
14-JUN-24 01-MAY-24
15-JUN-24 01-MAY-24
16-JUN-24 01-MAY-24
17-JUN-24 01-MAY-24
18-JUN-24 01-MAY-24
19-JUN-24 01-MAY-24
20-JUN-24 01-MAY-24
21-JUN-24 01-MAY-24
22-JUN-24 01-MAY-24
23-JUN-24 01-MAY-24
24-JUN-24 01-JUN-24
25-JUN-24 01-JUN-24
26-JUN-24 01-JUN-24
27-JUN-24 01-JUN-24
28-JUN-24 01-JUN-24
29-JUN-24 01-JUN-24
30-JUN-24 01-JUN-24
01-JUL-24 01-JUN-24
02-JUL-24 01-JUN-24
03-JUL-24 01-JUN-24
04-JUL-24 01-JUN-24
05-JUL-24 01-JUN-24
06-JUL-24 01-JUN-24
07-JUL-24 01-JUN-24
08-JUL-24 01-JUN-24
09-JUL-24 01-JUN-24
10-JUL-24 01-JUN-24
11-JUL-24 01-JUN-24
12-JUL-24 01-JUN-24
13-JUL-24 01-JUN-24
14-JUL-24 01-JUN-24
15-JUL-24 01-JUN-24
16-JUL-24 01-JUN-24
17-JUL-24 01-JUN-24
18-JUL-24 01-JUN-24
19-JUL-24 01-JUN-24
20-JUL-24 01-JUN-24
21-JUL-24 01-JUN-24
22-JUL-24 01-JUN-24
23-JUL-24 01-JUN-24
24-JUL-24 01-JUL-24
25-JUL-24 01-JUL-24
26-JUL-24 01-JUL-24
27-JUL-24 01-JUL-24
28-JUL-24 01-JUL-24
29-JUL-24 01-JUL-24
30-JUL-24 01-JUL-24
31-JUL-24 01-JUL-24
01-AUG-24 01-JUL-24
02-AUG-24 01-JUL-24
03-AUG-24 01-JUL-24
04-AUG-24 01-JUL-24
05-AUG-24 01-JUL-24
06-AUG-24 01-JUL-24
07-AUG-24 01-JUL-24
08-AUG-24 01-JUL-24

100 rows selected.

Rating

  (1 rating)

We're not taking comments currently, so please try again later if you want to add a comment.

Comments

Grouping by custom "month"

mathguy, August 05, 2024 - 5:24 pm UTC

From the way you asked the question, I will assume you would know how to group by customer and month, if the month was calendar month - and I will focus only on what is special about the "custom" month definition.

Here is code to generate the test data (which would have been a bit better if it included more than one customer; and, importantly, if it included gaps in the data - "custom" months with no orders from some customers, showing us what you would need in the output in that case):

create table orders (
  order_no    number primary key,
  customer_id number,
  order_date  date,
  amount      number
);

insert into orders (order_no, customer_id, order_date, amount)
  select 1001, 201, timestamp '2024-06-11 20:10:00', 2000 from dual union all
  select 1002, 201, timestamp '2024-06-25 15:35:00', 2100 from dual union all
  select 1003, 201, timestamp '2024-06-26 10:20:00', 1700 from dual union all
  select 1004, 201, timestamp '2024-07-15 20:58:00',  800 from dual union all
  select 1005, 201, timestamp '2024-07-25 13:45:00', 1500 from dual union all
  select 1006, 201, timestamp '2024-07-29 16:25:00',  900 from dual
;

commit;


I (ab?)used the TIMESTAMP literal - less writing than TO_DATE and a full format model; alas the DATE literal does not allow time component. Anyway, this is not important - it's just to create test data.

Note that DATE is an Oracle reserved keyword, so you shouldn't use it as a column name (you could, if you enclosed it in double-quotes - which is generally a poor practice). I used ORDER_DATE instead. Similar comment for the output - I used MTH instead of MONTH to avoid any confusion.

The trick is to use TRUNC(ORDER_DATE - 25, 'month') wherever you would otherwise use just TRUNC(ORDER_DATE, 'month'). The reason for this should be obvious. I believe this is what Connor was suggesting (his answer doesn't seem to address your question in its entirety).

In the SELECT list, I add one month to this (because apparently your "month" is based on the last day in the month, not on the first); and I apply TO_CHAR to control the output, so I can't use MTH from the SELECT list in ORDER BY. Alternatively (and the solution I would prefer) is to use ADD_MONTH(...) in the SELECT list, without applying formatting with TO_CHAR - and then format dates in the output from the user interface (NLS_DATE_FORMAT for example).

select customer_id, 
       to_char(add_months(trunc(order_date - 25, 'month'), 1), 'Mon-rr') as mth,
       sum(amount) as sum_amount
from   orders
group  by customer_id, trunc(order_date - 25, 'month')
order  by customer_id, trunc(order_date - 25, 'month')
;


Output is as requested:

CUSTOMER_ID MTH             SUM_AMOUNT
----------- --------------- ----------
        201 Jun-24                4100
        201 Jul-24                4000
        201 Aug-24                 900