无关系地映射两个表而没有循环

时间:2017-11-05 07:34:26

标签: sql sql-server tsql

我有一个复杂的问题需要解决而不使用循环。循环似乎非常简单。但是当试图在基于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

问题在于PaymentPlanTransaction表之间没有关系,我们无法创建它太复杂且系统是单片的。

我正在尝试创建一个名为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

以下是映射完成的细分方法。

  1. 2017-01-01客户付款$100,结果为TransId: 100 并且此transId被分配planId 100。为什么?因为这是 最早的计划。
  2. 2017-01-02客户再次付款$100生成TransId 200后再次分配到planId 100。为什么?因为它是 在步骤1中部分支付。planId 100的总金额为200美元。
  3. 2017-01-04客户付款$60并生成transId: 300,该planId: 200已分配给2017-02-02,因为此付款方案是下一个付款方式。
  4. $40生成此planId: 200 transId: 400的{​​{1}}客户付款planId: 200金额已生成并映射到planId: 300 and 400
  5. 客户在2017-04-02付款时迟到了$300他/她决定支付planId: 300 and 400,这将涵盖transId: 500。此付款产生1 planId 300 and 400。但是在映射表中,此事件为2017-04-10创建了两个条目。
  6. 最后一步! 2017-04-02 $200客户卡在planId:400付款时拒付,PaymentPlan只会下降。这导致了交易表中的逆转交易。然后,此事务将映射到映射表中的最新条目。如映射表中所示,transaction现在为-200。
  7. 以下是用于创建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 if (UIApplication.shared.statusBarOrientation == UIInterfaceOrientation.portrait) 表格的脚本。

        <?php foreach($allDiscussion as $data): ?>
            <div class="col s12 com_app_count_section">
                <script>count_app_and_com('<?=$data->discussion_id?>');</script>
            </div>
    <?php endforeach; ?>
    

    Here is a SQL fiddle

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

1 个答案:

答案 0 :(得分:1)

可以将两个表中的金额表示为连续范围,然后找到重叠范围。下一个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;