В SQL Server, как можно отслеживать распределение единиц? - PullRequest
0 голосов
/ 10 января 2020

Я пытаюсь создать повторно используемый запрос (хранимую процедуру), который будет вводить номер счета и дату и возвращать остаток единицы набора транзакций на основе количества транзакций, переведенных в эту дату или ранее:

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

Параметры:

@Account int = 200  
@Date date = 'Feb 1, 2020'

Счет 200

Account, trxid, transacted_units, transactiontype, transferfrom, transferto, date  
200, 3, 40, buy, NULL, NULL, 12/1/2019  
200, 4, 30, buy, NULL, NULL, 12/2/2019  
200, 5, 7, sell, NULL, NULL, 12/3/2019  
200, 6, 50, **transfer out**, NULL, 100, 1/2/2020  

В приведенном выше примере я ожидаю, что мой результаты должны быть: (Новый столбец units_bal)

Account, trxid, transacted_units, **units_bal**, transactiontype, transferfrom, transferto, date  
200, 3, 40, **33**, buy, NULL, NULL, 12/1/2019  
200, 4, 30, **17**, buy, NULL, NULL, 12/2/2019  
200, 5, 7, **0**, sell, NULL, NULL, 12/3/2019  
200, 6, **50**, NULL, **transfer out**, NULL, 100, 1/2/2020  

Шаг 1: Выполнить расчеты транзакций на основе FIFO для покупки и продажи - в порядке, есть две сделки покупки, затем продажа до передача происходит. Таким образом, продажа 7 единиц будет применяться к первой покупке 50 единиц, оставляя приемлемые единицы равными 43 для первой транзакции, а вторая покупка - нетронутой на уровне 30 единиц.

Шаг 2: из оставшихся отвечающих критериям единиц рассчитайте распределение 50 переданных единиц в порядке FIFO. Первая транзакция имеет 33 из 50 подходящих единиц, а вторая транзакция требует только 17 из 30 единиц для переноса.

Правило, согласно которому значение строки переноса всегда будет равно сумме всех баланс единиц в счете.

У меня есть код, который частично работает в этом примере, но две основные проблемы:

  1. Это очень неэффективно (мне нужно запустить его для записей 300k )
  2. Когда у меня несколько уровней входных и выходных переходов, происходит сбой
  3. При переводе из меньших единиц, чем имеет счет (частичный перевод), unit_bal неверен.
    • см. Примеры счетов 300, 400 и 900
SELECT *
INTO #Transactions
FROM (

    SELECT 200 account, 3 trxid, 40 transacted_units, 'buy' transactiontype, '' transferto, '' transferfrom, cast('2019-12-01' as date) [date]
    UNION ALL
    SELECT 200 account, 4 trxid, 30 transacted_units, 'buy' transactiontype, '' transferto, '' transferfrom, '2019-12-02' [date]
    UNION ALL
    SELECT 200 account, 5 trxid, 7 transacted_units, 'sell' transactiontype, '' transferto, '' transferfrom, '2019-12-03' [date]
    UNION ALL
    SELECT 200 account, 6 trxid, 63 transacted_units, 'tro' transactiontype, 100 transferto, '' transferfrom, '2020-01-01' [date]

    UNION ALL
    SELECT 300 account, 4 trxid, 250 transacted_units, 'buy' transactiontype, '' transferto, '' transferfrom, '2019-12-02' [date]
    UNION ALL
    SELECT 300 account, 4 trxid, 1000 transacted_units, 'buy' transactiontype, '' transferto, '' transferfrom, '2019-12-05' [date]
    UNION ALL
    SELECT 300 account, 5 trxid, 510 transacted_units, 'sell' transactiontype, '' transferto, '' transferfrom, '2019-12-10' [date]
    UNION ALL
    SELECT 300 account, 6 trxid, 100 transacted_units, 'tro' transactiontype, 500 transferto, '' transferfrom, '2020-01-05' [date]

    UNION ALL
    SELECT 400 account, 7 trxid, 2500 transacted_units, 'buy' transactiontype, '' transferto, '' transferfrom, '2019-12-10' [date]
    UNION ALL
    SELECT 400 account, 8 trxid, 2490 transacted_units, 'tro' transactiontype, 600 transferto, '' transferfrom, '2019-12-20' [date]

    UNION ALL
    SELECT 900 account, 9 trxid, 1200 transacted_units, 'buy' transactiontype, '' transferto, '' transferfrom, '2019-12-10' [date]
    UNION ALL
    SELECT 900 account, 10 trxid, 150 transacted_units, 'sell' transactiontype, '' transferto, '' transferfrom, '2019-12-11' [date]
    UNION ALL
    SELECT 900 account, 10 trxid, 300 transacted_units, 'tro' transactiontype, 1000 transferto, '' transferfrom, '2019-12-12' [date]


) a

/* Get Last record "Transfer Out" */
;with cte_last_record as (

    select *, last_value(trxid) over (partition by account order by [date] ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)  last_trxid
    from #Transactions

)

/* Get rank */
, cte_rnk as (

    select *,
        CASE 
            WHEN transacted_units*IIF(transactiontype='buy', 1, -1) < 0 THEN 0 --change sign of units out to negative
            ELSE ROW_NUMBER() OVER (PARTITION BY account ORDER BY transacted_units*IIF(transactiontype='buy', 1, -1) ASC, [date] ASC) 
        END rnk
    from cte_last_record

)

/* Get cumulative */
, cte_cumulative as (

    select *,
        case
            when trxid=last_trxid then 0
            else sum(iif(trxid=last_trxid, 0, transacted_units*IIF(transactiontype='buy', 1, -1))) over(partition by account order by rnk, [date]) 
        end cumulative
    from cte_rnk

)

select *,
        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 cte_cumulative
order by account, [date]

drop table #Transactions
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...