sqlsql-servert-sqlsql-server-2017

Split Invoice Total into multiple Rows but Split always equals Total


Create Table #Invoices
(
    InvoiceID int,
    Amount money,
    ProjectID int
)   
insert into #Invoices
(
    InvoiceID,
    Amount,
    ProjectID
)
select
    1,
    100,
    1
union all
select
    2,
    100,
    2
union all
select
    3,
    100,
    3
union all
select
    4,
    100,
    4

Create Table #Projects
(
    ProjectID int,
    AccountCode int
)
insert into #Projects
(
    ProjectID,
    AccountCode
)
select
    1,
    '12345'
union all
select
    2,
    '12345'
union all
select
    2,
    '7890'
union all
select
    3,
    '800'
union all
select
    3,
    '234'
union all
select
    3,
    '987'
union all
select
    4,
    '800'
union all
select
    4,
    '234'
union all
select
    4,
    '987'
union all
select
    4,
    '2579'

This is a follow-on question from this one from a few years ago Split 1 row into 2 rows with % of total

I have the above sample data, and what I'm trying to do is split the Invoice rows into multiple rows based off the AccountCode, but calculate a split, so if there are 2 related AccountCodes, the Amount should be split 50%/50%, if there are 3, then split 33.3%/33.3%/33.3%, but one line being 34 so it evens out to the total. And so on and so forth.

This is a query I've put together so far:

select 
    I.*,
    P.AccountCode,
    I.Amount / count(I.InvoiceID) over (partition by P.ProjectID) as SplitAmount
from 
    #Invoices I
Inner Join #Projects P on 
    I.ProjectID = P.ProjectID
order by
    I.InvoiceID

It seems to work in terms of the SplitAmount, but there is an issue. InvoiceID 3 contains three values of 33.33 which equals 99.99. How can I ensure the split will always equal the total amount regardless of the number of splits it has to?

Here is a table showing the end result I'm after. I've changed one of the rows to 33.34 so I get the 100 result, but bare in mind, the amount could be of any amount.

InvoiceID Amount ProjectID AccountCode SplitAmount
1 100.00 1 12345 100.00
2 100.00 2 12345 50.00
2 100.00 2 7890 50.00
3 100.00 3 800 33.33
3 100.00 3 234 33.33
3 100.00 3 987 33.34
4 100.00 4 800 25.00
4 100.00 4 234 25.00
4 100.00 4 987 25.00
4 100.00 4 2579 25.00

Solution

  • Sum up the SplitAmount and verify with the #Invoices.Amount and adjust any difference to one of the row.

    You might want to round() to 2 decimal places when calculating the SplitAmount

    select *,
           SplitAmount 
           + case when rn = 1 
                  then i.Amount - sum  (i.SplitAmount) 
                                  over (partition by i.ProjectID)
                  else 0
                  end  as AdjustedSplitAmount
    from
    (
      select 
          I.*,
          P.AccountCode,
          round(I.Amount / count(I.InvoiceID) over (partition by P.ProjectID), 2) as SplitAmount,
          -- for identifying one of row for adjustment
          row_number() over (partition by P.ProjectID order by p.AccountCode) as rn
      from 
          #Invoices I
      Inner Join #Projects P on 
          I.ProjectID = P.ProjectID
    ) i