postgresql:按 columns/windows function/min-max 分组和复杂查询

postgresql: group by columns/windows function/min-max and complex query

假设我有两家分行的发票。我需要 select 最小和最大发票日期,并在该日期显示分支 ID。如果在 min/max 日期有多个分支机构有发票,请选择任意一个。

CREATE TEMP TABLE invoice (
   id int not null,
   branch_id int  not null,
   c_date date  not null,
   PRIMARY KEY (id)
);

insert into invoice (id, branch_id, c_date) values
    (1, 1, '2020-01-01') 
    ,(2, 2, '2020-01-01')
    ,(3, 1, '2020-01-02')
    ,(4, 2, '2020-01-02')
    ,(5, 2, '2020-01-03');

直接的解决方案是(跳过最大部分以免查询过于复杂)。

select i2.branch_id, i2.c_date from (
    select min(i1.id) minid
    from (select min(i.c_date) mind, max(i.c_date) maxd 
        from invoice i
        )a
        join invoice i1
            on a.mind=i1.c_date) b 
    join invoice i2 on b.minid=i2.id

Window 函数解决方案有点简单但也很笨拙。请记住,实际查询比较复杂,我只提供核心部分。

select * from (
    select a.branch_id, a.c_date from(
        select *, rank() over (order by c_date) r from invoice i
    ) a where a.r=1
    limit 1
) mn,
(select a.branch_id, a.c_date from(
        select *, rank() over (order by c_date desc) r from invoice i
    ) a where a.r=1
    limit 1
) mx

有没有想过如何更优雅地编写查询?

一种方法是使用数组的技巧:

select min(date),
       (array_agg(branch_id order by date))[1] as first_branch,
       max(date),
       (array_agg(branch_id order by date desc))[1] as last_branch
from invoice;

这会将所有值聚合到一个数组中,因此如果每个结果行中的值太多,您就不会希望使用它。