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;
这会将所有值聚合到一个数组中,因此如果每个结果行中的值太多,您就不会希望使用它。
假设我有两家分行的发票。我需要 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;
这会将所有值聚合到一个数组中,因此如果每个结果行中的值太多,您就不会希望使用它。