Home > Net >  How to select the first 5 dates from each group and put them in a single column separated by comma i
How to select the first 5 dates from each group and put them in a single column separated by comma i

Time:12-01

I have a table like this:

Division Region Date of Last Visit
1 2 11/20/2021
1 2 11/18/2021
1 7 10/18/2021
1 7 11/19/2021
2 2 11/17/2021
2 3 09/20/2021
2 3 10/20/2021

I want to write a query that groups by the division and region columns and gives me the last 5 dates for each group separated by commas in a single column. Something like this:

Division Region Date of Last Visit Today Days since last visit
1 2 11/20/2021, 11/18/2021 sysdate sysdate - max(date of last visit)
1 7 10/18/2021, 11/19/2021 sysdate sysdate - max(date of last visit)
2 2 11/17/2021 sysdate sysdate - max(date of last visit)
2 3 9/20/2021, 10/20/2021 sysdate sysdate - max(date of last visit)

The last two columns are custom calculated columns that I also need for the final output table. Any help would be greatly appreciated as I have tried a lot of things but I keep getting errors about it not being grouped properly, possibly because of the two extra columns at the end. But even without that, I am not sure how to fetch only the last 5 dates per group in oracle.

Thanks!

CodePudding user response:

You want to filter the greatest-n-per-group using the ROW_NUMBER analytic function and then aggregate:

SELECT division,
       region,
       LISTAGG(TO_CHAR(date_of_last_visit, 'DD/MM/YYYY'), ',')
         WITHIN GROUP (ORDER BY date_of_last_visit DESC)
         AS date_of_last_visit,
       SYSDATE AS today,
       TRUNC(SYSDATE - MAX(date_of_last_visit)) AS days_since_last_visit
FROM   (
  SELECT t.*,
         ROW_NUMBER() OVER (PARTITION BY division, region
                            ORDER BY date_of_last_visit DESC) AS rn
  FROM   table_name t
)
WHERE  rn <= 5
GROUP BY
       division,
       region

Which, for the sample data:

CREATE TABLE table_name (division, region, date_of_last_visit) as
select 1, 2, date '2021-11-20' from dual union all
select 1, 2, date '2021-11-18' from dual union all
select 1, 7, date '2021-10-18' from dual union all
select 1, 7, date '2021-11-19' from dual union all
select 2, 2, date '2021-11-17' from dual union all
select 2, 3, date '2021-09-20' from dual union all
select 2, 3, date '2021-10-20' from dual;

Outputs:

DIVISION REGION DATE_OF_LAST_VISIT TODAY DAYS_SINCE_LAST_VISIT
1 2 20/11/2021,18/11/2021 30-NOV-21 10
1 7 19/11/2021,18/10/2021 30-NOV-21 11
2 2 17/11/2021 30-NOV-21 13
2 3 20/10/2021,20/09/2021 30-NOV-21 41

db<>fiddle here

CodePudding user response:

Here you go; read comments within code.

SQL> with test (division, region, datum) as
  2    -- sample data
  3    (select 1, 2, date '2021-11-20' from dual union all
  4     select 1, 2, date '2021-11-18' from dual union all
  5     select 1, 7, date '2021-10-18' from dual union all
  6     select 1, 7, date '2021-11-19' from dual union all
  7     select 2, 2, date '2021-11-17' from dual union all
  8     select 2, 3, date '2021-09-20' from dual union all
  9     select 2, 3, date '2021-10-20' from dual
 10    ),

 11  temp as
 12    -- rank rows per division/region, sorted by date column in descending order
 13    (select t.*,
 14       rank() over (partition by division, region order by datum desc) rnk
 15     from test t
 16    )
 17  -- select up to 5 last rows per division/region
 18  select division, region,
 19    listagg(datum, ', ') within group (order by datum) dates,
 20    trunc(sysdate) today,
 21    --
 22    (select trunc(sysdate) - a.datum
 23     from temp a
 24     where a.division = t.division
 25       and a.region = t.region
 26       and a.rnk = 1) days_since
 27  from temp t
 28  where rnk <= 5
 29  group by division, region
 30  order by division, region;

  DIVISION     REGION DATES                          TODAY      DAYS_SINCE
---------- ---------- ------------------------------ ---------- ----------
         1          2 11/18/2021, 11/20/2021         11/30/2021         10
         1          7 10/18/2021, 11/19/2021         11/30/2021         11
         2          2 11/17/2021                     11/30/2021         13
         2          3 09/20/2021, 10/20/2021         11/30/2021         41

SQL>
  • Related