简体   繁体   中英

Psql : Get Min, Max and Count records for each partner' invoice, and last payment

I have a table invoice like this :

id, partner_id, number, invoice_date

And a Payment Table like this:

id, payment_date, partner_id

I want to get min and max for both number & invoice_date, and count invoices, and last payment for each partner, something like this :

partner_id, min number, min date, max number, max date, count, last_pay
1, INV-2017-003, 02-01-2017, INV-2020-010, 01-01-2020, 142, 02-12-2019
5, INV-2019-124, 05-03-2019, INV-2020-005, 01-01-2020, 150, 01-01-2020
....

You can join those three tables including partners and grouping by parners' id column along with related aggregations :

 select pr.id, min(invoice_date), max(invoice_date), count(*), max(payment_date) as last_pay
   from partners pr
   left join invoices i on i.partner_id = pr.id
   left join payments p on p.partner_id = pr.id
  group by pr.id 

Update : You can use min() over () , max() over () and row_number() analytic functions to get the desired code depending on max and min dates :

select *
  from
 (
 select pr.id, 
        min(invoice_date) over (partition by pr.id order by invoice_date) as min_invoice_date, 
        max(invoice_date) over (partition by pr.id order by invoice_date desc) as max_invoice_date, 
        max(code) over (partition by pr.id order by invoice_date desc) as max_code,
        min(code) over (partition by pr.id order by invoice_date) as min_code,
        count(*)  over (partition by pr.id) as cnt, 
        max(payment_date) over (partition by pr.id) as last_pay,
        row_number() over (partition by pr.id order by invoice_date desc) as rn
   from partners pr
   left join invoices i on i.partner_id = pr.id
   left join payments p on p.partner_id = pr.id      
   ) q
 where rn = 1 

Why isn't this simple aggregation?

select i.partner_id,
       min(i.number) as min_number,
       min(i.invoice_date) as min_invoice_date,
       max(i.number) as min_number,
       max(i.invoice_date) as min_invoice_date,
       count(distinct i.invoice_id) as num_invoices,
       max(p.payment_date) as max_payment_date
from invoices i left join
     payments p
     on p.invoice_id = i.invoice_id
group by i.partner_id;

If you want the number on the earliest invoice (and min() doesn't work), then you can do this with a "first" aggregation function. Unfortunately, Postgres doesn't directly support one. But it does through array functions:

select i.partner_id,
       (array_agg(i.number order by i.invoice_date asc))[1] as min_number,
       min(i.invoice_date) as min_invoice_date,
       (array_agg(i.number order by i.invoice_date desc))[1] as min_number,
       max(i.invoice_date) as min_invoice_date,
       count(distinct i.invoice_id) as num_invoices,
       max(p.payment_date) as max_payment_date
from invoices i left join
     payments p
     on p.partner_id = i.partner_id
group by i.partner_id;

This is similar to @BarbarosÖzhan, but calculates the min/max before the join (if you got multiple rows per partner for both invoices and payments the COUNT will be wrong otherwise). Additionally there's only a single PARTTION/ORDER which should result in a more efficient plan.

SELECT i.*, p.last_pay
FROM
  ( -- 1st row has all the min values = filtered using row_number
    SELECT 
       partner_id
      ,number AS min_code
      ,invoice_date AS min_invoice_date
      -- value from row with max date
      ,Last_Value(number)
       Over (PARTITION BY partner_id
             ORDER BY invoice_date
             ROWS BETWEEN Unbounded Preceding AND Unbounded Following) AS max_code
      ,Last_Value(invoice_date)
       Over (PARTITION BY partner_id
             ORDER BY invoice_date
             ROWS BETWEEN Unbounded Preceding AND Unbounded Following) AS max_invoice_date
      ,Count(*)
       Over (PARTITION BY partner_id) AS Cnt
      ,Row_Number()
       Over (PARTITION BY partner_id ORDER BY invoice_date) AS rn
   FROM invoices
  ) AS i
LEFT JOIN
 ( -- max payment date per partner
   SELECT partner_id, Max(payment_date) AS last_pay
   FROM payments
   GROUP BY partner_id
 ) AS p
ON p.partner_id = i.partner_id      
WHERE i.rn = 1 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM