2015-05-20 11 views
5

Próbuję pokazać płatności stosowane do rachunków i chciałbym wiedzieć, czy jest sposób na to zrobić bez użycia kursorów i imperatywnej logiki. Mam tabelę Bills i tabelę płatności. Płatności nie zawsze są w wysokości rachunków, czasem przekraczają, czasem są niższe. Próbuję utworzyć powiązanie, które pokazuje, jaka część każdej płatności została zastosowana do każdego rachunku.Oparte na zestawie podejście do stosowania płatności na rachunki

Założenia:

  • Bills należeć do jednego konta
  • Płatności są stosowane do każdego rachunku w celu jego ID

Biorąc pod stół Bills:

ID Amount 
1 500 
2 500 
3 500 

Scenariusz 1:

Płatności stół

ID Amount 
1 750 
2 750 

z powyższym Bills stole i stół Scenariusz 1 płatności, chciałbym zobaczyć ten wynik:

Bill ID | Payment ID | Amount Applied 
1 1 500 
2 2 250 
3 2 500 
3 2 250 

Scenariusz 2:

Payments table: 
1 300 
2 300 
3 300 
4 300 
5 300 

Given powyżej tabeli Bills i scenariusz 2 Tabela płatności, chciałbym zobaczyć wyjście:

Bill ID | Payment ID | Amount Applied 
1 1 300 
1 2 200 
2 2 100 
2 3 300 
2 4 100 
3 4 200 
3 5 300 

Mogę to zrobić za pomocą kursorów, ale chciałbym się dowiedzieć, czy ktoś wie, jak to zrobić przy użyciu opartego na zestawach kodu SQL.

Dzięki!

+0

mogłem myśleć tylko o możliwie używając rekurencyjnej CTE, ale jeszcze nie zorientowali się dokładnie, jak ją stosować –

+1

Czy nie ma tabeli odnoszą się do płatności na rachunku? – TTeeple

+1

Czy jesteś w stanie zmienić swoje stoły? Zwykle będziesz miał stosunek do swojej tabeli płatności do tabeli rachunków, wiele do jednego. Powinno to zająć 2 tabele do reprezentowania twoich danych - prawidłowo znormalizowanych. – Christopher

Odpowiedz

0

SQL 2005 wymaga kilku dodatkowych kroków, ponieważ nie ma w nim funkcji SUM() OVER(ORDER BY...) i LAG(), ale podstawowy pomysł jest taki sam: obliczyć sumy bieżące rachunków i płatności.

Każdy rachunek może być w jednym z trzech stanów: nieopłacony, w pełni opłacony lub częściowo opłacony. Porównanie bieżących wartości określi stan. Różnica między obecnym a poprzednim stanem będzie równa wysokości płatności zastosowanej do tego rachunku w bieżącym cyklu.

SQL 2005

WITH 
    Bills_RunningTotal AS (
    SELECT 
     b_ID = b1.ID 
     ,b_Amount = b1.Amount 
     ,b_RunningTotal = SUM(b2.Amount) 
    FROM Bills b1 
    INNER JOIN Bills b2 
     ON (b2.ID <= b1.ID) 
    GROUP BY b1.ID,b1.Amount 
) 
,Payments_RunningTotal AS (
    SELECT 
     p_ID = p1.ID 
     ,p_Amount = p1.Amount 
     ,p_RunningTotal = SUM(p2.Amount) 
    FROM Payments p1 
    INNER JOIN Payments p2 
     ON (p2.ID <= p1.ID) 
    GROUP BY p1.ID,p1.Amount 
) 
,Bills_Payments_RemainderDue AS (
    SELECT 
     b_ID 
    ,p_ID 
    ,b_Previous_Remainder_Due = CASE 
     WHEN b_RunningTotal + p_Amount < p_RunningTotal THEN 0 
     WHEN b_RunningTotal + p_Amount > p_RunningTotal + b_Amount THEN b_Amount 
     ELSE b_RunningTotal + p_Amount - p_RunningTotal 
    END 
    ,b_Current_Remainder_Due = CASE 
     WHEN b_RunningTotal < p_RunningTotal THEN 0 
     WHEN b_RunningTotal > p_RunningTotal + b_Amount THEN b_Amount 
     ELSE b_RunningTotal - p_RunningTotal 
    END 
    FROM Bills_RunningTotal b 
    CROSS JOIN Payments_RunningTotal p 
) 
SELECT 
    [Bill ID] = b_ID 
,[Payment ID] = p_ID 
,[Amount Applied] = b_Previous_Remainder_Due - b_Current_Remainder_Due 
FROM Bills_Payments_RemainderDue 
WHERE b_Previous_Remainder_Due - b_Current_Remainder_Due > 0 
ORDER BY [Bill ID],[Payment ID] 

SQL 2008+

Korzystanie zamówione funkcje okienkowanych, zapytanie może być bardziej efektywne.

WITH 
    Bills_Payments_RunningTotal AS (
    SELECT 
     b_ID = b.ID 
     ,p_ID = p.ID 
     ,b_Amount = b.Amount 
     ,p_Amount = p.Amount 
     ,b_RunningTotal = SUM(b.Amount) OVER(PARTITION BY p.ID ORDER BY b.ID) 
     ,p_RunningTotal = SUM(p.Amount) OVER(PARTITION BY b.ID ORDER BY p.ID) 
    FROM Bills b 
    CROSS JOIN Payments p 
) 
,Bills_Payments_RemainderDue AS (
    SELECT b_ID,p_ID,b_Amount,p_Amount 
    ,b_Current_Remainder_Due = CASE 
     WHEN b_RunningTotal < p_RunningTotal THEN 0 
     WHEN b_RunningTotal > p_RunningTotal + b_Amount THEN b_Amount 
     ELSE b_RunningTotal - p_RunningTotal 
     END 
    FROM Bills_Payments_RunningTotal 
) 
,Bills_Payments_AmountApplied AS (
    SELECT 
     [Bill ID] = b_ID 
    ,[Payment ID] = p_ID 
    ,[Amount Applied] = ISNULL(LAG(b_Current_Remainder_Due) OVER(PARTITION BY b_ID ORDER BY p_ID),b_Amount) - b_Current_Remainder_Due 
    FROM Bills_Payments_RemainderDue 
) 
SELECT [Bill ID],[Payment ID],[Amount Applied] 
FROM Bills_Payments_AmountApplied 
WHERE [Amount Applied] > 0 
ORDER BY [Bill ID],[Payment ID] 
Powiązane problemy