Home > front end >  Return second highest date
Return second highest date

Time:10-24

I have a sales table, where it shows the information below

ID_sale sales_person sale_date
1 50 19/10/2022
2 43 17/9/2022
3 50 15/3/2022
4 43 13/2/2022
5 50 22/1/2022
6 10 05/2/2022
7 12 07/1/2022

and I want to create a query where I get the following information, basically the mosy recent date of the sale and the last sale date they made

ID_sale sales_person recent_sale last_sale
1 50 19/10/2022 15/3/2022
2 43 17/9/2022 13/2/2022
6 10 05/2/2022 05/2/2022
7 12 07/1/2022 07/1/2022

Thank you

CodePudding user response:

We use rank() to find the most recent couple of sales and then pivot the results.

select    max(recent_ID_sale)                         as ID_sale
         ,sales_person
         ,max(recent_sale)                            as recent_sale
         ,coalesce(max(last_sale), max(recent_sale))  as last_sale
from
(
select    sales_person
         ,case when rank() over(partition by sales_person order by sale_date desc) = 1 then sale_date end as recent_sale
         ,case when rank() over(partition by sales_person order by sale_date desc) = 2 then sale_date end as last_sale
         ,case when rank() over(partition by sales_person order by sale_date desc) = 1 then ID_sale   end as recent_ID_sale

from      t
) t
group by sales_person
order by recent_sale desc
ID_sale sales_person recent_sale last_sale
1 50 2022-10-19 2022-03-15
2 43 2022-09-17 2022-02-13
6 10 2022-02-05 2022-02-05
7 12 2022-01-07 2022-01-07

Fiddle

CodePudding user response:

You could add a CTE first where you add a row_nmber to get the order of the dates and then select them

update

I added a solution without GROUP BY and further window functions at the end

CREATE TABLE t
    (ID_sale int, sales_person int, sale_date date)
;
    
INSERT INTO t
    (ID_sale, sales_person, sale_date)
VALUES
    (1, 50, '2022-10-19'),
    (2, 43, '2022-9-17'),
    (3, 50, '2022-3-15'),
    (4, 43, '2022-2-13'),
    (5, 50, '2022-1-22'),
    (6, 10, '2022-2-5'),
    (7, 12, '2022-1-7')
;

Records: 7  Duplicates: 0  Warnings: 0
WITH CTE AS 
 ( select 
  ID_sale, sales_person, sale_date,
  ROW_NUMBER() OVER(PARTITION BY sales_person ORDER BY sale_date DESC) rn
from   t)
 SELECT
  MIN(ID_sale), sales_person,
  (SELECT sale_date FROM CTE WHERE sales_person = c1.sales_person and  rn = 1) recent_sale ,
  COALESCE(
  (SELECT sale_date FROM CTE WHERE sales_person = c1.sales_person and  rn = 2),
  (SELECT sale_date FROM CTE WHERE sales_person = c1.sales_person and  rn = 1)) last_sale 
  FROM CTE c1
  GROUP BY sales_person
  ORDER BY recent_sale DESC;

MIN(ID_sale) sales_person recent_sale last_sale
1 50 2022-10-19 2022-03-15
2 43 2022-09-17 2022-02-13
6 10 2022-02-05 2022-02-05
7 12 2022-01-07 2022-01-07
WITH CTE AS 
 ( select 
  ID_sale, sales_person, sale_date,
  ROW_NUMBER() OVER(PARTITION BY sales_person ORDER BY sale_date DESC) rn
from   t)
 SELECT
  ID_sale, sales_person,
  Sale_date  recent_sale ,
  COALESCE(
  (SELECT sale_date FROM CTE WHERE sales_person = c1.sales_person and  rn = 2),
  Sale_date) last_sale 
  FROM CTE c1
  WHERE rn = 1
  ORDER BY recent_sale DESC;
ID_sale sales_person recent_sale last_sale
1 50 2022-10-19 2022-03-15
2 43 2022-09-17 2022-02-13
6 10 2022-02-05 2022-02-05
7 12 2022-01-07 2022-01-07

fiddle

CodePudding user response:

Use ROW_NUMBER() to filter most recent rows and LEAD() for last_sale. Use COALESCE() for the case when no last_sale exists:

with cte as (
  select
    ID_sale,
    sales_person,
    sale_date as recent_sale,
    lead(sale_date) over w as last_sale,
    row_number() over w as rn
  from sales
  window w as (partition by sales_person order by sale_date desc)
)
select 
  ID_sale,
  sales_person,
  recent_sale,
  coalesce(last_sale, recent_sale) as last_sale
from cte
where rn = 1
order by ID_sale;

For older versions that don't support window function (like ROW_NUMBER() and LEAD()) you can use correlated (by sales_person) subqueries for last_sale and for the recent row filter in the WHERE clause:

select 
  s.ID_sale,
  s.sales_person,
  s.sale_date as recent_sale,
  coalesce((
    select
    max(sale_date)
    from sales s2
    where s2.sales_person = s.sales_person
      and s2.sale_date    < s.sale_date
  ), s.sale_date) as last_sale
from sales s
where sale_date = (
  select max(sale_date)
  from sales s1
  where s1.sales_person = s.sales_person
)
order by ID_sale;

CodePudding user response:

Not as efficient as a window function, but simpler and portable:

select max(t1.ID_sale), t1.sales_person, t1.recent_sale, max(t2.recent_sale) as last_sale
from mytable t1
left join mytable t2 on t2.sales_person = t1.sales_person
and t2.ID_sale < t1.ID_sale
group by 2, 3

Unless you have 100's of millions of sales, this will perform OK if you have an index on sales_person.

  • Related