通过TSQL使用后进先出法计算结果
我想用TSQL通过后进先出法进行计算 使用后进先出法将要求您通过卖出最后一笔交易来计算损益 其工作原理示例: 交易于3月1日达成。我们以每股5美元的价格购买10只股票 交易于3月2日达成。我们以每股6美元的价格购买15只股票 交易于3月3日达成。我们以每股4美元的价格购买5只股票 交易于3月4日达成。我们以每股7美元的价格出售17只股票 到第四笔交易时,我们已经从3月3日起以每股4美元的价格出售了5只股票,从3月2日起以每股6美元的价格出售了12只股票 因此,现在我们留下以下内容: 3月1日交易后,10支股票,每股5美元 3只股票,3月2日17-5-15的交易价格为每股6美元 剩下13只股票,平均价格为10*5+3*6/13=5.23076923 以下是测试数据生成脚本:通过TSQL使用后进先出法计算结果,sql,tsql,sql-server-2012,sql-scripts,lifo,Sql,Tsql,Sql Server 2012,Sql Scripts,Lifo,我想用TSQL通过后进先出法进行计算 使用后进先出法将要求您通过卖出最后一笔交易来计算损益 其工作原理示例: 交易于3月1日达成。我们以每股5美元的价格购买10只股票 交易于3月2日达成。我们以每股6美元的价格购买15只股票 交易于3月3日达成。我们以每股4美元的价格购买5只股票 交易于3月4日达成。我们以每股7美元的价格出售17只股票 到第四笔交易时,我们已经从3月3日起以每股4美元的价格出售了5只股票,从3月2日起以每股6美元的价格出售了12只股票 因此,现在我们留下以下内容: 3月1日交易
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
最好的方法是什么?因为您的示例非常狭窄,所以很难制定出防弹解决方案。但这会让你走上正确的轨道,或者至少是一条轨道。它使用一种反向运行总计,然后从库存量中减去。使用数据集时只需做一点小改动:
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
我怀疑你可能会想要交错买卖,所以虽然我认为另一个答案是一个很好的起点,但它不会完全处理整个场景 基本上,我认为你必须用某种迭代机制来处理这个问题。我试着用递归实现,但不幸的是,解析函数不能正确地使用这种方法。所以我回到临时工作台,边走边循环
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
我把它剪了下来以便邮寄。请在此处查看它的实际操作,并提供更多输出,以便更好地遵循逻辑: