T-SQL中FIFO利润的计算
我需要一些关于单个查询的帮助或指针,这些查询可以根据FIFO(先进先出)原则在下面附带的数据集上为每个股票交易建立损益。应与SQL Server 2016+和Azure SQL兼容 示例数据如下所示: 股票名称 交易日期 档码 量 价格美元 总金额美元 股票ABC 2017-12-11 11:16:11.000 购买 2364,444444 0,114323 270,310382 斯托克斯 2017-12-11 11:16:11.000 购买 2364,444444 0,114323 270,310382 股票ABC 2017-12-14 14:16:24.000 卖 1000 0,158849 158,849 斯托克斯 2017-12-14 14:16:24.000 卖 1000 0,158849 158,849 股票ABC 2017-12-14 19:38:46.000 卖 700 0,198934 139,2538 斯托克斯 2017-12-14 19:38:46.000 卖 700 0,198934 139,2538 股票ABC 2017-12-15 09:38:09.000 卖 664,4444444 0,207171 137,65362T-SQL中FIFO利润的计算,sql,sql-server,tsql,Sql,Sql Server,Tsql,我需要一些关于单个查询的帮助或指针,这些查询可以根据FIFO(先进先出)原则在下面附带的数据集上为每个股票交易建立损益。应与SQL Server 2016+和Azure SQL兼容 示例数据如下所示: 股票名称 交易日期 档码 量 价格美元 总金额美元 股票ABC 2017-12-11 11:16:11.000 购买 2364,444444 0,114323 270,310382 斯托克斯 2017-12-11 11:16:11.000 购买 2364,444444 0,114323 270,3
谢谢你的反馈,让我做一些改变
SELECT Currency,TransactionDate,
SUM(CASE WHEN TranCode = 'SELL' THEN CostBasis * (-1) ELSE CostBasis END )OVER(PARTITION BY Currency ORDER BY TransactionDate ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS TOTAL
FROM #Stock
GROUP BY Currency, TransactionDate, CostBasis, TranCode
下面是一种简单的方法(排序),它使用一个名为dbo.fnTally
的函数为每个项目或数量的“份额”生成一行。“购买”和“出售”都使用CROSS-APPLY
dbo.fnTally(1,s.Quantity)来扩展每个数量项目的行。此外,两个CTE
都分配了一个称为“trans”的顺序行编号。通过将“trans\rn”上的两个CTE
,只需将价格相加即可计算出FIFO
的盈利能力。像这样的
dbo.fnTally
CREATE FUNCTION [dbo].[fnTally]
/**********************************************************************************************************************
Jeff Moden Script on SSC: https://www.sqlservercentral.com/scripts/create-a-tally-function-fntally
**********************************************************************************************************************/
(@ZeroOrOne BIT, @MaxN BIGINT)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN WITH
H2(N) AS ( SELECT 1
FROM (VALUES
(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
)V(N)) --16^2 or 256 rows
, H4(N) AS (SELECT 1 FROM H2 a, H2 b) --16^4 or 65,536 rows
, H8(N) AS (SELECT 1 FROM H4 a, H4 b) --16^8 or 4,294,967,296 rows
SELECT N = 0 WHERE @ZeroOrOne = 0 UNION ALL
SELECT TOP(@MaxN)
N = ROW_NUMBER() OVER (ORDER BY N)
FROM H8;
仅供参考,自2008年以来,您已经能够在
INSERT
语句中使用VALUES
表结构;无需使用昂贵的UNION
,也无需再使用较便宜的UNION ALL
。此外,在INSERT
语句中,数字列不应放在单引号中-这只会导致不必要的字符串转换,然后再回到相应的数值-只需使用158.849作为成本基础
-无单引号!嗨,谢谢你的建议。但这并不能解决我需要的问题。请阅读“我需要一个计算,它计算每个交易的利润。例如,如果我以5的价格购买10支股票ABC,然后以6的价格购买10支,然后以9的价格出售所有15支股票,我需要计算利润,将购买价格考虑在内。”这意味着我需要看到每次出售的利润/亏损,根据第一次购买的价格,@BennyChristiansen您好,我已经更新了答案,请检查并告诉我是否还有其他问题。再次感谢你。再次感谢,但数字似乎不匹配。以“2017-12-14 19:38:00.000”中的“卖出”为例。以0.198934000000000000的价格出售700个数量,从2017-12-11 11 11:16:00.000以0.114323000000000的价格批量购买,在您的计算中给出负利润。这应该是正利润,因为700辆车的购买价格几乎是其销售价格的一半。:)我已经用了两天的时间在这上面,没有找到任何东西:好吧,那么把‘卖’改为‘买’怎么样。非常感谢。这正是我想要的。非常感谢!
;with
buy_cte(Currency, TransactionDate, Price, trans_rn) as (
select Currency, TransactionDate, Price,
row_number() over (partition by Currency order by TransactionDate)
from #Stock s
cross apply dbo.fnTally(1, s.Quantity) fn
where TranCode='BUY'),
sell_cte(Currency, TransactionDate, Price, trans_rn) as (
select Currency, TransactionDate, Price,
row_number() over (partition by Currency order by TransactionDate)
from #Stock s
cross apply dbo.fnTally(1, s.Quantity) fn
where TranCode='SELL')
select s.Currency, s.TransactionDate,
cast(sum(b.price) as decimal(14,2)) buy_sum,
cast(sum(s.price) as decimal(14,2)) sell_sum,
cast(sum(s.price-b.price) as decimal(14,2)) profit_sum,
count(*) q_sold
from buy_cte b
join sell_cte s on b.Currency=s.Currency
and b.trans_rn=s.trans_rn
and b.TransactionDate<s.TransactionDate
group by s.Currency, s.TransactionDate
order by TransactionDate;
Currency TransactionDate buy_sum sell_sum profit_sum q_sold
StockABC 2017-12-14 14:16:00.000 114.32 158.85 44.53 1000
StockABC 2017-12-14 19:38:00.000 80.03 139.25 59.23 700
StockABC 2017-12-15 09:38:00.000 75.91 137.56 61.65 664
StockABC 2017-12-21 21:02:00.000 163.50 158.24 -5.26 334
StockABC 2017-12-26 10:45:00.000 195.32 174.92 -20.40 399
StockABC 2017-12-30 11:34:00.000 244.76 316.88 72.12 500
StockABC 2018-01-03 17:45:00.000 9.79 21.13 11.34 20
StockABC 2018-03-21 15:42:00.000 633.45 319.92 -313.53 1472
StockABC 2018-04-16 07:53:00.000 73.92 105.97 32.05 450
StockABC 2018-04-24 20:15:00.000 75.44 136.96 61.52 460
StockABC 2018-07-22 17:08:00.000 66.53 87.15 20.62 500
StockABC 2018-08-05 17:34:00.000 133.00 127.40 -5.60 1000