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]
mogłem myśleć tylko o możliwie używając rekurencyjnej CTE, ale jeszcze nie zorientowali się dokładnie, jak ją stosować –
Czy nie ma tabeli odnoszą się do płatności na rachunku? – TTeeple
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