繁体   English   中英

如何在 BigQuery 中确定给定账单和付款数据的付款日期

[英]How to determine date paid given billing and payment data in BigQuery

我需要知道何时支付账单以确定支付的早晚时间。 不幸的是,我只有账单创建数据和付款记录。

示例 1

--raw data
WITH bill AS (
  SELECT 'b1' AS id, DATE('2022-01-01') AS created, DATE('2022-01-15') AS due, 50 AS amount UNION ALL
  SELECT 'b2', DATE('2022-01-01'), DATE('2022-01-30'), 50 UNION ALL
  SELECT 'b3', DATE('2022-01-03'), DATE('2022-01-17'), 50 UNION ALL
  SELECT 'b4', DATE('2022-01-03'), DATE('2022-02-01'), 50 UNION ALL
  SELECT 'b5', DATE('2022-01-05'), DATE('2022-01-19'), 50 UNION ALL
  SELECT 'b6', DATE('2022-01-05'), DATE('2022-02-04'), 50
),
payment AS (
  SELECT 'p1' AS id, DATE('2022-01-10') AS made, 50 AS amount UNION ALL
  SELECT 'p2', DATE('2022-01-11'), 50
)
-- setup
SELECT * FROM bill

我想要一个查询,它返回账单表中的所有数据以及支付账单的日期,这在理论上是从支付表中派生的。

在上面的示例中,解决方案可能是按到期日对账单行进行排序并相应地应用付款:

--raw data
WITH bill AS (
  SELECT 'b1' AS id, DATE('2022-01-01') AS created, DATE('2022-01-15') AS due, 50 AS amount UNION ALL
  SELECT 'b2', DATE('2022-01-01'), DATE('2022-01-30'), 50 UNION ALL
  SELECT 'b3', DATE('2022-01-03'), DATE('2022-01-17'), 50 UNION ALL
  SELECT 'b4', DATE('2022-01-03'), DATE('2022-02-01'), 50 UNION ALL
  SELECT 'b5', DATE('2022-01-05'), DATE('2022-01-19'), 50 UNION ALL
  SELECT 'b6', DATE('2022-01-05'), DATE('2022-02-04'), 50
),
payment AS (
  SELECT 'p1' AS id, DATE('2022-01-10') AS made, 50 AS amount UNION ALL
  SELECT 'p2', DATE('2022-01-11'), 50
),
--start solution
p AS (
  SELECT
    payment.id,
    payment.made,
    payment.amount,
    SUM( payment.amount ) OVER (
      ORDER BY payment.made
    ) AS amount_cumulative
  FROM payment
),
b AS (
  SELECT
    bill.id,
    bill.created,
    bill.due,
    bill.amount,
    SUM( bill.amount ) OVER (
      ORDER BY bill.due
    ) AS amount_cumulative
  FROM bill
),
repayments AS (
  SELECT
    b.*,
    p.made,
    ROW_NUMBER() OVER (
      PARTITION BY b.id
      ORDER BY p.made ASC
    ) AS seq
  FROM b
  LEFT JOIN p
    ON b.amount_cumulative <= p.amount_cumulative
  WHERE p.made IS NOT NULL
)
SELECT
  b.*,
  repayments.made AS payment_date
FROM b
LEFT JOIN repayments
  ON b.id = repayments.id
WHERE (repayments.seq = 1 OR repayments.seq IS NULL)
ORDER BY b.id

正确的答案,但错误的解决方案

示例 2

但是,如果我们更改某些帐单和付款日期,该解决方案就会失效(因为付款必须应用于具有未结余额和付款时最早到期日的帐单)。 例如:

--raw data
WITH bill AS (
  SELECT 'b1' AS id, DATE('2022-01-01') AS created, DATE('2022-01-15') AS due, 50 AS amount UNION ALL
  SELECT 'b2', DATE('2022-01-01'), DATE('2022-01-30'), 50 UNION ALL
  SELECT 'b3', DATE('2022-01-03'), DATE('2022-01-17'), 50 UNION ALL
  SELECT 'b4', DATE('2022-01-03'), DATE('2022-02-01'), 50 UNION ALL
  SELECT 'b5', DATE('2022-01-05'), DATE('2022-01-19'), 50 UNION ALL
  SELECT 'b6', DATE('2022-01-05'), DATE('2022-02-04'), 50
),
payment AS (
  SELECT 'p1' AS id, DATE('2022-01-02') AS made, 100 AS amount UNION ALL
  SELECT 'p2', DATE('2022-01-11'), 50
),
--start solution
p AS (
  SELECT
    payment.id,
    payment.made,
    payment.amount,
    SUM( payment.amount ) OVER (
      ORDER BY payment.made
    ) AS amount_cumulative
  FROM payment
),
b AS (
  SELECT
    bill.id,
    bill.created,
    bill.due,
    bill.amount,
    SUM( bill.amount ) OVER (
      ORDER BY bill.due
    ) AS amount_cumulative
  FROM bill
),
repayments AS (
  SELECT
    b.*,
    p.made,
    ROW_NUMBER() OVER (
      PARTITION BY b.id
      ORDER BY p.made ASC
    ) AS seq
  FROM b
  LEFT JOIN p
    ON b.amount_cumulative <= p.amount_cumulative
  WHERE p.made IS NOT NULL
)
SELECT
  b.*,
  repayments.made AS payment_date
FROM b
LEFT JOIN repayments
  ON b.id = repayments.id
WHERE (repayments.seq = 1 OR repayments.seq IS NULL)
ORDER BY b.id

解决方案有问题

如果p1amount = 75(在这种情况下b2应保持未完全支付,但b3应在2022-01-11全额支付),则此示例更加棘手。

问题

我可以通过查询获得payment_date吗? 如果是这样,那看起来像什么?

请参考下面的查询。 想法是在一定范围内分配付款。

--raw data
WITH bill AS (
  SELECT 'b1' AS id, DATE('2022-01-01') AS created, DATE('2022-01-15') AS due, 50 AS amount UNION ALL
  SELECT 'b2', DATE('2022-01-01'), DATE('2022-01-30'), 50 UNION ALL
  SELECT 'b3', DATE('2022-01-03'), DATE('2022-01-17'), 50 UNION ALL
  SELECT 'b4', DATE('2022-01-03'), DATE('2022-02-01'), 50 UNION ALL
  SELECT 'b5', DATE('2022-01-05'), DATE('2022-01-19'), 50 UNION ALL
  SELECT 'b6', DATE('2022-01-05'), DATE('2022-02-04'), 50
),
payment AS (
  SELECT 'p1' AS id, DATE('2022-01-10') AS made, 50 AS amount UNION ALL
  SELECT 'p2', DATE('2022-01-11'), 50
), b as (
-- setup
  SELECT
    bill.id,
    bill.created,
    bill.due,
    bill.amount,
    SUM( bill.amount ) OVER (
      ORDER BY bill.created, bill.due
    ) AS amount_cumulative_created,
    SUM( bill.amount ) OVER (
      ORDER BY bill.due
    ) as amount_cummulative_due,
    row_number() over (order by bill.due) crn
  FROM bill
  order by created
), p as (
   SELECT
    p.id,
    p.made,
    p.amount,
    case when 
      row_number() over (order by 0) = 
      max(un) over (partition by p.id) then
        floor(p.amount / max(un) over (partition by p.id)) + 
        mod(p.amount, max(un) over (partition by p.id))
      else
        floor(p.amount / max(un) over (partition by p.id)) 
    end new_amount,
    row_number() over (order by 0) prn
  FROM 
    (select *,
    row_number() over (order by payment.made) prn 
    from payment) p join b
    on p.prn = b.crn
    
,unnest(generate_array(1,
(select coalesce(max(b.crn),0)+1 from b
where p.amount > b.amount_cummulative_due
and p.made between b.created and b.due),
1)) un
), p_distrib as (
  select *,
    SUM( p.new_amount ) OVER (
      ORDER BY p.prn
    ) AS payment_cumulative, from p
)
select b.*, 
(select min(pd.made) from p_distrib pd
 where
--- Adjust here for what date takes preference - created vs. due.
--- Replace amount_cumulative_created  with amount_cumulative_due as needed
 b.amount_cumulative_created <= pd.payment_cumulative
 and pd.made between b.created and b.due)
 from b;
 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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