根据其他表行值将一行拆分为两行

时间:2016-05-11 16:10:37

标签: sql join sql-server-2012

我有两张表格如下

第一张表

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循环。还有别的办法吗?打开任何建议。

1 个答案:

答案 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