Postgresql 重叠日期范围和求和结果

Postgresql overlapping date ranges and summing results

我正在尝试将 link 一份 table 合同添加到交易列表中,以查看是否有任何超支,但是,数据没有有效的、唯一的共享密钥。

这是我的合同示例 table:

| buyer_id | supplier_id | start_date | end_date   | contract_value  |
| buyer_a  | supplier_a  | 2015-01-01 | 2017-01-01 | 240000          |
| buyer_a  | supplier_a  | 2016-01-01 | 2016-06-01 | 6000            |
| buyer_a  | supplier_b  | 2015-01-01 | 2015-12-31 | 100000          |
| buyer_a  | supplier_b  | 2017-01-01 | 2017-12-31 | 100000          |

这是我的支出示例 table:

| buyer_id | supplier_id | month      | trans_value    |
| buyer_a  | supplier_a  | 2015-01-01 | 1230.12        |
| buyer_a  | supplier_a  | 2015-02-01 | 1735.98        |
| buyer_a  | supplier_a  | 2015-03-01 | 2242.02        |

因为合同日期重叠(例如与 supplier_a 的合同),我不能只 link 每个合同每个月的所有交易,因为这意味着我是双倍的计算重叠期间的交易。

同样,我不能使用 max() 和 min(),因为这样在合同期间发生的任何交易(例如带有 supplier_b 的交易)都将被包括在内。

据我所知,link 这些 table 的最佳方法是将我的合同 table 汇总到一个视图中,使其看起来像这样.. .

| buyer_id | supplier_id | month      | value |
| buyer_a  | supplier_a  | 2015-01-01 | 10000 |
| buyer_a  | supplier_a  | 2015-02-01 | 10000 |
| buyer_a  | supplier_a  | 2015-03-01 | 10000 |
| buyer_a  | supplier_a  | 2015-04-01 | 10000 |
| buyer_a  | supplier_a  | 2015-05-01 | 10000 |
| buyer_a  | supplier_a  | 2015-06-01 | 10000 |
| buyer_a  | supplier_a  | 2015-07-01 | 10000 |

只要每个月的价值是合约的总份额,就很容易link buyer_idsupplier_idmonth,然后我可以识别任何超支。

问题是我什至不知道如何构建新视图。我觉得我应该能够使用子查询 'unpack' 将日期范围转换为月份列表,然后使用 sum(case()) 之类的东西,但我力不从心。

ps。我无法控制这些数据的发布方式,因此无法从源头改进数据。

编辑:我希望能够创建这样的输出,然后我可以将其放入图表中以显示超支:

| buyer_id | supplier_id | month      | monthly_con_val | trans_value |
| buyer_a  | supplier_a  | 2015-01-01 | 10000           | 34000       |
| buyer_a  | supplier_a  | 2015-02-01 | 10000           | 10000       |
| buyer_a  | supplier_a  | 2015-03-01 | 50000           | 8000        |
| buyer_a  | supplier_a  | 2015-04-01 | 50000           | 14000       |
| buyer_a  | supplier_a  | 2015-05-01 | 50000           | 4000        |
| buyer_a  | supplier_a  | 2015-06-01 | 10000           | 3000        |
| buyer_a  | supplier_a  | 2015-07-01 | 10000           | 3000        |

类似

with
  -- Sample data
  contracts(bs_id, start_date, end_date, contract_value) as (values
    (1, '2015-01-01'::date, '2017-01-01'::date, 240000),
    (1, '2016-01-01'::date, '2016-06-01'::date, 6000)),
  spending(bs_id, month, trans_value) as (values
    (1, '2015-01-01'::date, 1230.12),
    (1, '2015-02-01'::date, 1735.98),
    (1, '2016-05-01'::date, 5689.01)),
  -- End of sample data
  contracts_monthly as (
    select
      bs_id,
      month::date,
      sum(
        contract_value / (
          (extract(year from end_date)*12 + extract(month from end_date)) - 
          (extract(year from start_date)*12 + extract(month from start_date)))) as monthly_con_val
    from contracts, generate_series(start_date, end_date, interval '1 month') as month
    group by bs_id, month
    order by bs_id, month)
select
  *
from
  contracts_monthly left join spending using (bs_id, month);

为了使示例更紧凑,我将列 buyer_id | supplier_id 合并为单个列 bs_id

About generate_series() function