sql >> Base de Datos >  >> RDS >> Sqlserver

Fila de grupo por suma de columna específica igual a valor específico

Con una tabla auxiliar que define los límites "Vencidos" impuestos sobre la tabla de origen con el total acumulado calculado, se puede calcular una intersección de cada importe total acumulado con los límites "Vencidos":

With Receipt As ( --< Sample source table
    Select * From (Values
        ('R1', 100),
        ('R2', 100),
        ('R3',  70),
        ('R4',  50),
        ('R5', 200)
    ) V (ReceiptNo, Amount)
), ReceiptWithTotal As ( --< Source table with Running Totals calculated
    Select *,
        SUM(Amount) Over (Order By ReceiptNo Rows Unbounded Preceding) - Amount As RunningTotalBefore,
        SUM(Amount) Over (Order By ReceiptNo Rows Unbounded Preceding) As RunningTotalAfter
    From Receipt
), Due As ( --< Helper table to define intervals (can be generated dynamically to cover any Total)
    Select * From (Values
        ('D1',   0, 100),
        ('D2', 100, 200),
        ('D3', 200, 300),
        ('D4', 300, 400),
        ('D5', 400, 500),
        ('D6', 500, 600)
    ) V (DueNo, AmountLow, AmountHigh)
)
Select DueNo, ReceiptNo,
    IIF(AmountHigh < RunningTotalAfter, AmountHigh, RunningTotalAfter) -
    IIF(AmountLow > RunningTotalBefore, AmountLow, RunningTotalBefore) As Amount
From Due
Inner Join ReceiptWithTotal On NOT (RunningTotalAfter <= AmountLow OR RunningTotalBefore >= AmountHigh)

Tenga en cuenta:SUM(...) Over (Order By ... Rows Unbounded Preceding) y IIF(...) están disponibles solo en SQL Server 2012+. Lo mismo se puede hacer en SQL Server 2008 a través de una subconsulta, aunque mucho menos eficiente:

With Receipt As ( --< Sample source table
    Select * From (Values
        ('R1', 100),
        ('R2', 100),
        ('R3',  70),
        ('R4',  50),
        ('R5', 200)
    ) V (ReceiptNo, Amount)
), ReceiptWithTotal As ( --< Source table with Running Totals calculated
    Select *, RunningTotalAfter - Amount As RunningTotalBefore
    From (
        Select *,
            (Select SUM(Amount) From Receipt B Where B.ReceiptNo <= A.ReceiptNo) As RunningTotalAfter
        From Receipt A
    ) A
), Due As ( --< Helper table to define intervals (can be generated dynamically to cover any Total)
    Select * From (Values
        ('D1',   0, 100),
        ('D2', 100, 200),
        ('D3', 200, 300),
        ('D4', 300, 400),
        ('D5', 400, 500),
        ('D6', 500, 600)
    ) V (DueNo, AmountLow, AmountHigh)
)
Select DueNo, ReceiptNo,
    CASE WHEN AmountHigh < RunningTotalAfter THEN AmountHigh ELSE RunningTotalAfter END -
    CASE WHEN AmountLow > RunningTotalBefore THEN AmountLow ELSE RunningTotalBefore END As Amount
From Due
Inner Join ReceiptWithTotal On NOT (RunningTotalAfter <= AmountLow OR RunningTotalBefore >= AmountHigh)