我有两张表格如下
CID Cmonth Amount
---------------------------
1 JAN 15000
2 JAN 5000
3 JAN 5000
4 FEB 20000
5 MAR 15000
SerialNo EmpID Cmonth Amount
--------------------------------------
1 111 JAN 11000
2 222 JAN 7000
3 333 JAN 7000
4 111 FEB 10000
5 222 FEB 5000
6 333 FEB 5000
7 111 MAR 10000
8 222 MAR 5000
9 333 MAR 5000
SerialNo EmpID Cmonth Amount CID
-----------------------------------------------
1 111 JAN 11000 1
2 222 JAN 4000 1
2 222 JAN 3000 2
3 333 JAN 2000 2
3 333 JAN 5000 3
4 111 FEB 10000 4
5 222 FEB 5000 4
6 333 FEB 5000 4
7 111 MAR 10000 5
8 222 MAR 5000 5
9 333 MAR 5000 5
我不想使用游标或while循环。还有别的办法吗?打开任何建议。
答案 0 :(得分:1)
这可以让你在那里大部分时间。使用递归CTE可能有更简单/更快/更好的方法来做到这一点,但这种方法是我尝试过的第一件事。
目前有一个" bug",但你似乎没有在你的问题中解决这个问题。 3月的水桶只有15000个,但当月的总量为20000个。在您的最终输出中,您已将最后一笔金额分配给最后一位CID,但不能从任何解释中清除,所以现在我已将此保留原样:
;WITH CTE_RunningTotals AS
(
SELECT
SerialNo,
EmpID,
Cmonth,
Amount,
SUM(Amount) OVER (PARTITION BY Cmonth ORDER BY SerialNo) AS RunningAmount
FROM
Deposits
),
CTE_RunningTotalsWithLast AS
(
SELECT SerialNo, EmpID, Cmonth, Amount, RunningAmount, LAG(RunningAmount, 1, 0) OVER (PARTITION BY Cmonth ORDER BY SerialNo) AS LastRunningAmount FROM CTE_RunningTotals
),
CTE_LimitTotals AS
(
SELECT CID, Cmonth, SUM(Amount) OVER (PARTITION BY Cmonth ORDER BY CID) AS RunningAmount FROM Limits
),
CTE_LimitTotalsWithLast AS
(
SELECT
CID, Cmonth, RunningAmount,
LAG(RunningAmount, 1, 0) OVER (PARTITION BY Cmonth ORDER BY CID) AS LastRunningAmount,
LAG(CID) OVER (PARTITION BY Cmonth ORDER BY CID) AS LastCID
FROM
CTE_LimitTotals
)
SELECT
D.SerialNo,
D.EmpID,
D.Cmonth,
CASE WHEN D.Amount < D.RunningAmount - L.LastRunningAmount THEN D.Amount ELSE D.RunningAmount - L.LastRunningAmount END AS Amount,
L.CID
FROM
CTE_RunningTotalsWithLast D
INNER JOIN CTE_LimitTotalsWithLast L ON
L.Cmonth = D.Cmonth AND
D.RunningAmount > L.LastRunningAmount AND
D.RunningAmount <= L.RunningAmount
UNION ALL
SELECT
D.SerialNo,
D.EmpID,
D.Cmonth,
L.LastRunningAmount - D.LastRunningAmount,
L.LastCID AS CID
FROM
CTE_RunningTotalsWithLast D
INNER JOIN CTE_LimitTotalsWithLast L ON
L.Cmonth = D.Cmonth AND
D.LastRunningAmount < L.LastRunningAmount AND
D.RunningAmount >= L.LastRunningAmount
ORDER BY
D.SerialNo