MS SQL FIFO Частичные переводы - PullRequest
1 голос
/ 08 января 2020

У меня есть ряд транзакций, которые переносят инвентарь с одного счета на другой. Я могу передать весь инвентарь, и я могу передать частичный инвентарь.

Мне нужно заплатить комиссию владельцу счета, на котором находится инвентарь, на дату моей комиссии.

В моем отчете необходимо указать исходное происхождение предметов инвентаря, если они были переданы, и предоставить unit_balance, из которого я могу рассчитать комиссию.

Пример транзакций:

Счет 100
Счет, trxid, transacted_units, тип транзакции, Transferfrom, Transferto, дата
100, 1, 100, купить, NULL, NULL, 1/1/2020
100, 2, 50, передача в, 200, NULL, 1/2/2020

Счет 200
Счет, trxid, transacted_units, тип транзакции, TransferFrom, Transferto, дата
200, 3, 40, купить, NULL, NULL, 12/1/2019
200, 4 , 30, купить, NULL, NULL, 12/2/2019
200, 5, 7, продать, NULL, NULL, 12/3/2019
200, 6, 50, вывести, NULL, 100, 1/2/2020


В моих выходных данных отчета должны отображаться полные сведения об учетных записях, связанных с инвентаризацией, связанной с unit_bala nce

Выходные данные отчета:
[уровень], Account, trxid, parenttrxid, transacted_units, тип транзакции, TransferFrom, Transferto, Date, units_balance
0, 100, 1, NULL , 100, купить, NULL, NULL, 1/1/2020, 100
0, 100, 2, NULL, 50, передача в, 200, NULL, 1/2/2020, NULL
1, 200, 3, 2, 40, купить, NULL, NULL, 01.12.2009, 33
1, 200, 4, 2, 30, купить, NULL, NULL, 12.12.2009, 17
1, 200, 5, 2, 7, продажа, NULL, NULL, 03.12.2009, 0
1, 200, 6, 2, 50, выписка, NULL, 100, 1/2/2020, 0

* Лог FIFO c применяет 7 единиц, проданных к первой покупке для счета 200. Затем при переводе необходимо рассчитать unit_balance по оставшимся приемлемым транзакциям.

SQL код I «Сегодня» работает только тогда, когда я перевожу полную сумму инвентаря, а не частичную передачу:

    select
        [level],
        parentid,
        trxid,
        account,
        transactiontype,
        date,
        rnk,
        transacted_units,
        cumulative,
        CASE 
            WHEN cumulative>0 and transacted_units>=cumulative THEN cumulative
            WHEN cumulative>0 and transacted_units<cumulative THEN transacted_units 
            ELSE 0 
        END units_bal
    from (
        select
            *, 
            sum(transacted_units*Positive_Negative_Indicator) over (partition by parenttrxid, account order by rnk, date, trxid RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) cumulative
        from (

            select *,       
                CASE 
                    WHEN transacted_units*Positive_Negative_Indicator < 0 THEN 0 
                    ELSE ROW_NUMBER() OVER (PARTITION BY parenttrxid, account ORDER BY Positive_Negative_Indicator ASC, date ASC, trxid ASC) 
                END rnk
            from Transactions

        ) a
    ) a

Поле positive_negative_indicator представляет направление транзакции. Продажа или передача отрицательны, а остальные положительны.

1 Ответ

0 голосов
/ 08 января 2020

для каждой текущей транзакции «in», вычислить промежуточный итог (в единицах) для предыдущих транзакций «in». Затем назначьте столько единиц «out», которые не были использованы предыдущими транзакциями «in» (столько блоков «out» == с общим количеством единиц «out», которые могут быть использованы текущей транзакцией «in») .

declare @t table
(
    Account int, 
    trxid int,
    trunits int,
    trtype varchar(20),
    transfrom int,
    transto int,
    thedate date
);

insert into @t(Account, trxid, trunits, trtype, transfrom, transto, thedate)
values
(100, 1, 100, 'buy', NULL, NULL, '20200101'),
(100, 2, 50, 'transfer in', 200, NULL, '20200201'),
(200, 3, 40, 'buy', NULL, NULL, '20190112'),
(200, 4, 30, 'buy', NULL, NULL, '20190213'),
(200, 5, 10, 'buy', NULL, NULL, '20190214'),
(200, 6, 7, 'sell', NULL, NULL, '20190315'),
(200, 7, 9, 'sell', NULL, NULL, '20190316'),
(200, 8, 25, 'buy', NULL, NULL, '20190317'),
(200, 9, 39, 'sell', NULL, NULL, '20190318'),
(200, 10, 18, 'sell', NULL, NULL, '20190319'),
(200, 11, 14, 'sell', NULL, NULL, '20190320'),
(200, 11, 50, 'transfer out', NULL, 100, '20200201');



select *, case when t.trtype not in ('sell', 'transfer out') then t.trunits -isnull(otu.out_units, 0) else null end as leftover_units
from 
(
select *, sum(case when trtype not in ('sell', 'transfer out') then trunits else 0 end) over (partition by Account order by thedate rows between unbounded preceding and 1 preceding) as previous_in_running_units
from @t
) as t
outer apply
(
    select top (1) ort.out_units_running_total - isnull(t.previous_in_running_units, 0) as out_units
    from
    (
        select sum(o.trunits) over(order by o.thedate) as out_units_running_total
        from @t as o
        where o.trtype in ('sell', 'transfer out')
        and o.Account = t.Account
        and t.trtype not in ('sell', 'transfer out') --no calculations needed when cross applying for "out" transactions
    ) as ort --out running totals
    where ort.out_units_running_total-isnull(t.previous_in_running_units, 0) <= t.trunits --<-- ("in") use as many out units as can be consumed by current t.transaction/date after deducting what has been consumed by the previous t.transaction/date
    and ort.out_units_running_total-isnull(t.previous_in_running_units, 0) > 0 --not needed(?) if balance is guaranteed.. total_out = total_in 
    order by ort.out_units_running_total desc 
) as otu; --"out units" 
...