3

我想用 TSQL 通过 LIFO(后进先出)方法进行计算。

使用 LIFO 方法将要求您通过卖出最后一笔交易来计算盈亏。

示例它是如何工作的:

  1. 交易于 3 月 1 日完成,我们以 5 美元的价格购买 10 只股票
  2. 交易于 3 月 2 日完成,我们以每只 6 美元的价格购买 15 只股票
  3. 交易于 3 月 3 日完成,我们以每只 4 美元的价格购买 5 只股票
  4. 交易于 3 月 4 日完成,我们以每只 7 美元的价格出售 17 只股票

到第四次交易时,我们现在已经卖出了 3 月 3 日起的 5 只股票,每只 4 美元,3 月 2 日起的 12 只股票每只 6 美元。

所以现在我们留下了以下内容:3 月 1 日交易的 10 只股票,每只 5 美元 3 月 2 日交易的每只 6 美元的 3 只股票(17-5-15 = -3)。

剩余 13 只股票,平均价格为 (10*5 + 3*6) / 13 = 5.23076923

这是测试数据生成脚本:

use TestTask
go
IF OBJECT_ID('testtable','U')IS NOT NULL
        DROP TABLE testtable
go
create table testtable
(
stockid int not null,
dealid int identity (1,1) not null,
dealtype char(1) not null,
stockdate datetime not null,
stockamount int not null,
priceperstock int not null
)
insert into testtable(stockid,dealtype,stockdate,stockamount,priceperstock)
VALUES
    (111,'B','01.03.2016',10,5),
    (111,'B','02.03.2016',15,6),
    (111,'B','03.03.2016',5,4),
    (111,'S','04.03.2016',17,7)

我想计算财务状况和许多其他参数,这些参数需要我知道还剩多少合适价格的股票。到目前为止,我已经做到了这一点:

select
stockid,
dealid,
dealtype,
stockdate,
priceperstock,
case dealtype
    when 'B' then stockamount
    when 'S' then -stockamount
end as stockamount,
sum(
        case dealtype
            when 'B' then stockamount
            when 'S' then -stockamount
        end
    ) over (partition by 
        stockid order by dealid ROWS UNBOUNDED PRECEDING)
         as poistion
from testtable

输出:

stockid dealid  dealtype       stockdate           priceperstock    stockamount    poistion
    111       1       B       2016-01-03 00:00:00.000      5             10             10
    111       2       B       2016-02-03 00:00:00.000      6             15             25
    111       3       B       2016-03-03 00:00:00.000      4             5              30
    111       4       S       2016-04-03 00:00:00.000      7            -17             13

期望的输出:

stockid dealid  dealtype       stockdate           priceperstock    stockamount    poistion    stocksleft
    111       1       B       2016-01-03 00:00:00.000      5             10             10      10
    111       2       B       2016-02-03 00:00:00.000      6             15             25      3
    111       3       B       2016-03-03 00:00:00.000      4             5              30      0
    111       4       S       2016-04-03 00:00:00.000      7            -17             13      0

最好的方法是什么?

4

2 回答 2

2

由于您的示例非常狭窄,因此很难组合出一个防弹解决方案。但这应该让你开始走上正确的轨道,或者至少是一条轨道。它使用一种反向运行总计,然后从库存量中减去。使用您的数据集稍作改动:

create table #testtable
(
stockid int not null,
dealid int identity (1,1) not null,
dealtype char(1) not null,
stockdate datetime not null,
stockamount int not null,
priceperstock int not null
)
 insert into #testtable(stockid,dealtype,stockdate,stockamount,priceperstock)
    VALUES
        (111,'B','01.03.2016',10,5),
        (111,'B','02.03.2016',15,6),
        (111,'B','03.03.2016',5,4),
        (111,'S','04.03.2016',-17,7) --signed int

----Add this to see another level 
 --insert into #testtable(stockid,dealtype,stockdate,stockamount,priceperstock)
 --   VALUES
 --       (111,'S','05.03.2016',-12,5)
    ;WITH CTE
    AS (
        SELECT stockid
            , dealid
            , dealtype
            , stockdate
            , priceperstock
            , stockamount
            , sum(stockamount) OVER (
                ORDER BY dealid DESC
                ) AS runningtotal
            , sum(stockamount) OVER (
            ORDER BY dealid) AS position
        FROM #testtable
        )
    SELECT stockid
        , dealid
        , dealtype
        , stockdate
        , priceperstock
        , stockamount
        --, runningtotal
        , position
        , CASE 
            WHEN dealtype = 'S' 
                THEN 0
            WHEN stockamount > runningtotal AND runningtotal < 0
                THEN 0
            WHEN stockamount > runningtotal AND runningtotal >= 0
                THEN runningtotal
            WHEN stockamount < runningtotal
                THEN stockamount
            END AS StockRemaining
    FROM cte
    ORDER BY dealid
于 2016-07-26T16:33:56.053 回答
1

我怀疑您可能想要交错买卖,所以虽然我认为另一个答案是一个很好的起点,但它并不能完全处理整个场景。

基本上我认为你必须用某种迭代机制来处理这个问题。我试图用递归来做到这一点,但不幸的是,分析函数不能用这种方法正常工作。所以我回到了一个临时表和while循环。

create table #R (
    lvl int not null, stockId int not null, dealId int not null,
    stockDate datetime not null, stockAmount int not null, pricePerStock int not null,
    stockRemaining int not null, amountDeducted int not null
);

insert into #R (
    lvl, stockId, dealId, stockDate, stockAmount,
    pricePerStock, stockRemaining, amountDeducted
)
select 0, stockId, dealId, stockDate, stockAmount, pricePerStock, stockAmount, 0
from <T> where dealtype = 'B' /* <--- your table is <T> */

declare @lvl int = 0;
declare @rowCount int = 1;
while @rowCount > 0
begin
    set @lvl = @lvl + 1;
    with sells as (
        select stockId, dealId as saleId,
            row_number() over (order by dealId) as sellNum, stockAmount as sellAmount
        from <T> where dealType = 'S'
    )
    update #R
    set stockRemaining = (
        select stockRemaining
        from (
            select dealId,
                case
                    when r.stockRemaining + s.sellAmount
                          < sum(stockRemaining) over (order by dealId desc)
                        then r.stockRemaining
                    when sum(stockRemaining) over (order by dealId desc)
                          < s.sellAmount
                        then 0
                    else sum(stockRemaining) over (order by dealId desc)
                          - s.sellAmount
                end as stockremaining
            from sells s inner join #R r
                on r.stockId = s.stockId and r.dealId < s.saleId
            where s.stockId = #R.stockId and s.sellNum = @lvl
        ) data
        where dealId = #R.dealId
    )
    where dealId < (select saleId from sells where sellNum = @lvl);
    set @rowCount = @@rowCount;
end

我已将其修剪下来以供发布。在这里查看它的实际效果,并提供更多输出以更好地遵循逻辑:http ://rextester.com/WPLKLJ95730

于 2016-07-26T23:27:13.030 回答