sql – 没有关系且没有循环映射两个表

我有一个复杂的问题要解决而不使用循环.循环似乎非常简单.但是当试图在基于Set的操作中思考时,它被证明是非常棘手的.

细节

以下是PaymentPlan表,我存储了每个客户的付款计划.
例如:客户需要支付多少费用以及支付日期.

PlanId |PaymentAmount   |CustomerId |StartDate  
100    |200.00          |100        |2017-01-01 
200    |100.00          |100        |2017-02-01
300    |100.00          |100        |2017-03-01
400    |200.00          |100        |2017-04-01

如上表所示,它包含customerId 100的所有付款计划.

接下来我有一个名为Transaction的表.此表存储了上述付款计划的交易.

TransId |CustomerId |Amount |TransactionDate |IsReversed
100     |100        |100.00 |2017-01-01      |0
200     |100        |100.00 |2017-01-02      |0
300     |100        |60.00  |2017-01-04      |0
400     |100        |40.00  |2017-02-02      |0
500     |100        |300.00 |2017-04-02      |0
600     |100        |200.00 |2017-04-10      |1

问题是PaymentPlan和Transaction Table之间没有关系,我们无法创建一个太复杂的系统,而且系统是单片的.

我正在尝试创建一个名为TransPaymentPlanMapping的新表
将以下列格式存储两个表之间的映射.
使用循环创建映射并不难,但性能不会很好.我无法提出基于集合的解决方案.

CustomerId  |transId    |PlanId |RunningPaidAmount  |transDateTime  |IsReversed
100         |100        |100    |100                |2017-01-01     |0
100         |200        |100    |200                |2017-01-02     |0
100         |300        |200    |60                 |2017-01-04     |0
100         |400        |200    |100                |2017-02-02     |0
100         |500        |300    |100                |2017-04-02     |0
100         |500        |400    |200                |2017-04-02     |0
100         |600        |400    |-200               |2017-04-10     |1

以下是映射如何完成的细分.

>在2017-01-01,客户支付100美元,结果是TransId:100
并且这个transId被分配了planId 100.为什么?因为这是
最早的计划.
>在2017-01-02客户再次支付100美元生成TransId
200再次分配给planId 100.为什么?因为它是
在第1步中部分支付.lightId 100的总金额为200美元.
> 2017-01-04客户支付60美元并生成transId:300,分配给planId:200,因为此付款计划是下一个.
>在2017-02-02上,客户为planId支付40美元的金额:200支付此支付的费用:生成400并映射到planId:200.
>客户在计划时支付的时间较晚:300和400在2017-04-02他/她决定支付300美元,其中包括planId:300和400.此付款产生1 transId:500.但在映射表中此事件为planId 300和400创建两个条目.
>最后一步!在2017-04-10客户卡拒绝他在2017-04-02上的付款,它只下降200美元.这导致了交易表中的逆转交易.然后,此事务将映射到映射表中的最新条目.如映射表中所示,planId:400现在为-200.

以下是创建PaymentPlan和事务表的脚本.

CREATE TABLE #PaymentPlan(PlanId        INT ,
                          PaymentAmount  NUMERIC(8,2),
                          CustomerId     INT,
                          StartDate     DATETIME)
INSERT #PaymentPlan( 
        PlanId ,
        PaymentAmount ,
        CustomerId ,
        StartDate )
VALUES  ( 100,  200.00, 100, '2017-01-01'),
        ( 200,  100.00, 100, '2017-02-01'),
        ( 300,  100.00, 100, '2017-03-01'),
        ( 400,  200.00, 100, '2017-04-01')


CREATE TABLE #transaction(TransId         INT,
                          CustomerId      INT,
                          Amount          NUMERIC(8,2),
                          TransactionDate DATETIME,
                          IsReversed BIT)
INSERT #transaction( 
       TransId ,
       CustomerId ,
       Amount ,
       TransactionDate ,
       IsReversed)
VALUES  (100,100,100.00,'2017-01-01',0),
        (200,100,100.00,'2017-01-02',0),
        (300,100,60.00 ,'2017-01-04',0),
        (400,100,40.00, '2017-02-02',0),
        (500,100,300.00,'2017-04-02',0),
        (600,100,200.00,'2017-04-10',1)

SELECT * FROM #PaymentPlan ORDER BY StartDate 
SELECT * FROM #transaction ORDER BY TransactionDate

Here is a SQL fiddle

非常感谢我能得到的任何帮助.

最佳答案 可以将两个表中的金额表示为连续范围,然后找到重叠范围.下一个
snippet可能是更精细版本的基础.

CREATE TABLE #PaymentPlan(PlanId        INT,
                          PaymentAmount NUMERIC(8, 2),
                          CustomerId    INT,
                          StartDate     DATETIME);
INSERT #PaymentPlan(
        PlanId,
        PaymentAmount,
        CustomerId,
        StartDate)
VALUES ( 100,  200.00, 100, '2017-01-01'),
       ( 200,  100.00, 100, '2017-02-01'),
       ( 300,  100.00, 100, '2017-03-01'),
       ( 400,  200.00, 100, '2017-04-01');
CREATE TABLE #transaction(TransId         INT,
                          CustomerId      INT,
                          Amount          NUMERIC(8,2),
                          TransactionDate DATETIME,
                          IsReversed      BIT);
INSERT #transaction( 
       TransId,
       CustomerId,
       Amount,
       TransactionDate,
       IsReversed)
VALUES (100,100,100.00,'2017-01-01',0),
       (200,100,100.00,'2017-01-02',0),
       (300,100,60.00 ,'2017-01-04',0),
       (400,100,40.00, '2017-02-02',0),
       (500,100,200.00,'2017-04-02',0),
       (600,100,300.00,'2017-04-10',1);
WITH
  p AS (
    SELECT *,
      SUM(PaymentAmount) OVER(PARTITION BY CustomerId ORDER BY StartDate) - PaymentAmount LeftBound,
      SUM(PaymentAmount) OVER(PARTITION BY CustomerId ORDER BY StartDate) RightBound
    FROM #PaymentPlan
  ),
  t AS (
    SELECT *,
      SUM(Amount) OVER(PARTITION BY CustomerId ORDER BY TransactionDate) - Amount LeftBound,
      SUM(Amount) OVER(PARTITION BY CustomerId ORDER BY TransactionDate) RightBound
    FROM #transaction
  )
SELECT
  t.CustomerId, t.TransId, p.PlanId,
  p.LeftBound PlanLeftBound, p.RightBound PlanRightBound,
  t.LeftBound TransLeftBound, t.RightBound TransRightBound,
  t.TransactionDate transDateTime, t.IsReversed
FROM p JOIN t
  ON p.CustomerId = t.CustomerId AND
     p.LeftBound < t.RightBound AND
     p.RightBound > t.LeftBound;
点赞