将两个表聚合成时间序列
Posted
技术标签:
【中文标题】将两个表聚合成时间序列【英文标题】:Aggregating two tables into time series 【发布时间】:2020-06-05 22:48:26 【问题描述】:假设我有两个表,它们成对地保存面向时间的数据(日期、数据)。其中一个存放我的日常开支,另一个存放我的日常收入。日期表示该费用/收入率的开始时间。
例如:如果我在收入表中有两条记录:(2020-01-01, 50), (2020-02-14, 100) 表示在 2020-01-01 和 2020-02-14 之间我的收入是每天 50 个单位,2020-02-14 之后的收入是每天 100 个单位。费用也是如此。
我想在 postgres 表中为给定时间间隔生成一个时间序列(可能使用 time generate_series(date,date,interval) 函数),其中一行如下所示: (天、收入、费用)
例如,如果我的收入表如下所示:
+------------+--------+
| date | income |
+------------+--------+
| 2020-02-12 | 50 |
| 2020-02-14 | 100 |
+------------+--------+
还有这样的费用:
+------------+--------------+
| date | expenses |
+------------+--------------+
| 2020-02-12 | 70 |
| 2020-02-13 | 50 |
+------------+--------------+
我希望从 2020-02-12 到 2020-02-15 间隔的结果如下所示:
+------------+--------+--------------+
| day | income | expenses |
+------------+--------+--------------+
| 2020-02-12 | 50 | 70 |
| 2020-02-13 | 50 | 50 |
| 2020-02-14 | 100 | 50 |
| 2020-02-15 | 100 | 50 |
+------------+--------+--------------+
以便稍后我可以计算我的利润、损失和其他统计数据。我怎样才能做到这一点?
【问题讨论】:
你知道日期是独一无二的吗? 【参考方案1】:这有点棘手。您可以在日期上full join
,但随后您需要填补空白。 Postgres 不支持窗口函数上的ignore nulls
,因此一种选择是使用条件和来构建组,然后first_value()
:
select
date,
first_value(income) over(partition by grp_i order by date) income,
first_value(expense) over(partition by grp_e order by date) expense
from (
select
date,
i.income,
e.expense,
count(*) filter(where i.income is not null) over(order by date) grp_i,
count(*) filter(where e.expense is not null) over(order by date) grp_e
from incomes i
full join expenses e using(date)
) t
另一方面,您也可以从选定的日期期间开始(使用generate_series()
),然后将带有left join
s 的表格带入。其余逻辑不变:
select
date,
first_value(income) over(partition by grp_i order by date) income,
first_value(expense) over(partition by grp_e order by date) expense
from (
select
d.date,
i.income,
e.expense,
count(*) filter(where i.income is not null) over(order by d.date) grp_i,
count(*) filter(where e.expense is not null) over(order by d.date) grp_e
from generate_series(date '2020-02-12', date '2020-02-15', interval '1' day) d(date)
left join incomes i on i.date = d.date
left join expenses e on e.date = d.date
) t
order by date
【讨论】:
【参考方案2】:一种方法(如果您的数据不太大)是横向连接:
select gs.dte, i.income, e.expense
from generate_series('2020-02-12'::date, '2020-02-15'::date, interval '1 day'
) gs(dte) left join lateral
(select i.*
from income i
where i.date <= gs.dte
order by i.date desc
limit 1
) i
on true left join lateral
(select e.*
from expense e
where e.date <= gs.dte
order by e.date desc
limit 1
) e
on true;
另一种选择是在每个表上独立使用generate_series()
来计算值。两者都从最早的日期开始,所以这是可行的:
select i.date, i.income, e.expense
from (select gs.date, i.income
from (select i.*, lead(date) over (order by date) as next_date
from income i
) i cross join lateral
generate_series(date, coalesce(next_date - interval '1 day', '2020-02-15'::date), interval '1 day') gs(date)
) i join
(select gs.date, e.expense
from (select e.*, lead(date) over (order by date) as next_date
from expense e
) e cross join lateral
generate_series(date, coalesce(e.next_date - interval '1 day', '2020-02-15'::date), interval '1 day') gs(date)
) e
on i.date = e.date;
Here 是两个解决方案的 dbfiddle。
这很容易修改以处理两个系列不在同一日期开始的情况。
【讨论】:
以上是关于将两个表聚合成时间序列的主要内容,如果未能解决你的问题,请参考以下文章