2016-10-06 13 views
1

T-SQLのように見ている中で、実行中の減算を実行:(彼らは一時、テーブルだが)私は2つのテーブルに座っている

CREATE TABLE [dbo].[Invoice] 
(
    [InvoiceId]  [int] NOT NULL, 
    [ReceiverId] [int] NOT NULL, 
    [Amount]  [numeric](19, 2) NOT NULL, 
    [Priority]  [int] NOT NULL 
); 
GO 

CREATE TABLE [dbo].[Payment] 
(
    [PaymentId] [int] NOT NULL, 
    [SenderId] [int] NOT NULL, 
    [Amount] [numeric](19, 2) NOT NULL 
); 
GO 

データは、このようなものを見ることができます請求書

InvoiceId ReceiverId Amount Priority 
     1   1 100.00   1 
     2   1 100.00   2 
     3   2 100.00   1 
     4   2 100.00   2 
     5   1 200.00   3 

支払い

PaymentId SenderId  Amount 
     1   1  50.00 
     2   1  45.00 
     3   2  95.00 
     4   2  105.00 

入金はPaymentに格納されます。私のコードの仕事は、送信者の請求書の間にPayment.Amountを配布することです。

この2つの関係キーは、ReceiverIdSenderIdです。

にはPriority列が一意であり、値「1」は「2」よりも高い優先度を持ちます。

SenderId「1」と Payment行が ReceiverId「1」とインボイスの数が無限に使用することができ

- それらのすべてのためのPayment.Amount欄に足りない場合、彼らは彼らのPriorityに従って支払われます。

私はループやカーソルを使わずにこれをプログラムする方法を考えようとしています。助言がありますか? (私はSQL Server 2014に座っています)。

私の予想される出力は次のようになります。

1) Payment 1 and 2 would be used to partially pay Invoice 1. 
2) Payment 3 would be used to partially pay Invoice 3. 
3) Payment 4 would then complete invoice 3. 
4) Payment 4 would then completely pay invoice 4. 
5) Invoice 2 and 5 would be left completely unpaid. 
+0

あなたは答えを待っている間、あなたは[これを熟読することをお勧めします](http:// stackoverflow。com/questions/9420173/sql-subtracting-a-depleting-from-rows/9421009#9421009)質問。 – HABO

+1

SQL Server 2014を質問に追加します。 – Ize

+0

サンプルデータを使用するだけで、期待した結果が得られますか? – jyao

答えて

3

数直線上の間隔としてあなたのドル金額の主なアイデア

と思います。請求書と支払いを正しい順序で、互いに隣接する行に配置します。(それらと交差)と共に間隔の両方のセットを入れ

請求書、受信機/送信者ID = 1

|----100---|----100---|--------200--------|-----------> 
0   100  200     400 
ID 1   2    5 

支払い、受信機/送信者ID = 1

|-50-|-45|--------------------------------------------> 
0 50 95 
ID 1 2 

|----|---|-|----------|-------------------|-----------> 
0 50 95 100  200     400 

の今、あなたが持っている間隔:


請求書

From To InvoiceID PaymentID 
------------------------------------ 
    0 50   1   1 
    50 95   1   2 
    95 100   1 
100 200   2 
200 400   5 
、レシーバ/送信者ID = 2

|----100---|----100---|-------------------------------> 
0   100  200     
ID 3   4 

支払い、レシーバ/送信者ID = 2

|--95----|-----105----|-------------------------------> 
0  95   200 
ID 3   4 

両方のセットの間隔を合わせる(交差する裾):

|--------|-|----------|-------------------------------> 
0  95 100  200     

は、今あなたが持っている間隔:()にも何もないことができ、最大で1枚の請求書と最大で1つのお支払いが可能これらの間隔のそれぞれについて

From To InvoiceID PaymentID 
------------------------------------ 
    0 95   3   3 
    95 100   3   4 
100 200   4   4 

。これらの各期間に対応する請求書と支払いを確認し、請求書と支払いの間のマッピングを取得します。各請求書の支払い間隔を合計すると、請求書の全額または一部が支払われたかどうかがわかります。


インボイスとペイメントの間隔の初期リストを作成することは、合計を実行することによって行われます。

SUM(Amount) OVER (PARTITION BY ReceiverId ORDER BY Priority 
    ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS InvoiceInterval 

SUM(Amount) OVER (PARTITION BY SenderId ORDER BY PaymentID 
    ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS PaymentInterval 

これらの2つのセットを交差させることは簡単ですUNIONです。

各区間について、対応する請求書および支払いを見つける。それを行う簡単な方法の1つはOUTER APPLYのサブクエリです。

これらをまとめてみましょう。

サンプルデータ

DECLARE @Invoice TABLE 
(
    [InvoiceId]  [int] NOT NULL, 
    [ReceiverId] [int] NOT NULL, 
    [Amount]  [numeric](19, 2) NOT NULL, 
    [Priority]  [int] NOT NULL 
); 

DECLARE @Payment TABLE 
(
    [PaymentId] [int] NOT NULL, 
    [SenderId] [int] NOT NULL, 
    [Amount] [numeric](19, 2) NOT NULL 
); 

INSERT INTO @Invoice(InvoiceId,ReceiverId,Amount,Priority) VALUES 
(1, 1, 100.00, 1), 
(2, 1, 100.00, 2), 
(3, 2, 100.00, 1), 
(4, 2, 100.00, 2), 
(5, 1, 200.00, 3); 

INSERT INTO @Payment(PaymentId, SenderId, Amount) VALUES 
(1, 1, 50.00), 
(2, 1, 45.00), 
(3, 2, 95.00), 
(4, 2, 105.00); 

クエリ

WITH 
CTE_InvoiceIntervals 
AS 
(
    SELECT 
     I.InvoiceId 
     ,I.ReceiverId AS ClientID 
     ,I.Priority 
     ,SUM(I.Amount) OVER (PARTITION BY I.ReceiverId ORDER BY I.Priority 
      ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS InvoiceInterval 
    FROM @Invoice AS I 
) 
,CTE_PaymentIntervals 
AS 
(
    SELECT 
     P.PaymentId 
     ,P.SenderId AS ClientID 
     ,P.PaymentId AS Priority 
     ,SUM(P.Amount) OVER (PARTITION BY P.SenderId ORDER BY P.PaymentID 
      ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS PaymentInterval 
    FROM @Payment AS P 
) 
,CTE_AllIntervals 
AS 
(
    SELECT 
     ClientID 
     ,InvoiceInterval AS Interval 
    FROM CTE_InvoiceIntervals 

    UNION 

    SELECT 
     ClientID 
     ,PaymentInterval AS Interval 
    FROM CTE_PaymentIntervals 
) 
SELECT * 
FROM 
    CTE_AllIntervals 
    OUTER APPLY 
    (
     SELECT TOP(1) CTE_InvoiceIntervals.InvoiceId 
     FROM CTE_InvoiceIntervals 
     WHERE 
      CTE_InvoiceIntervals.ClientID = CTE_AllIntervals.ClientID 
      AND CTE_InvoiceIntervals.InvoiceInterval >= CTE_AllIntervals.Interval 
     ORDER BY 
      CTE_InvoiceIntervals.InvoiceInterval 
    ) AS A_Invoices 
    OUTER APPLY 
    (
     SELECT TOP(1) CTE_PaymentIntervals.PaymentId 
     FROM CTE_PaymentIntervals 
     WHERE 
      CTE_PaymentIntervals.ClientID = CTE_AllIntervals.ClientID 
      AND CTE_PaymentIntervals.PaymentInterval >= CTE_AllIntervals.Interval 
     ORDER BY 
      CTE_PaymentIntervals.PaymentInterval 
    ) AS A_Payments 
ORDER BY 
    ClientID 
    ,Interval; 

結果

+----------+----------+-----------+-----------+ 
| ClientID | Interval | InvoiceId | PaymentId | 
+----------+----------+-----------+-----------+ 
|  1 | 50.00 |   1 | 1   | 
|  1 | 95.00 |   1 | 2   | 
|  1 | 100.00 |   1 | NULL  | 
|  1 | 200.00 |   2 | NULL  | 
|  1 | 400.00 |   5 | NULL  | 
|  2 | 95.00 |   3 | 3   | 
|  2 | 100.00 |   3 | 4   | 
|  2 | 200.00 |   4 | 4   | 
+----------+----------+-----------+-----------+ 
+0

あなたは優れたソリューションを投稿しただけでなく、非常によく説明しました!ありがとう! – Ize

+0

@Ize、あなたは歓迎です –