在SQL查询中计算收益

问题描述

我有过去10年中多家公司的股价数据。我希望能够查询此表以返回这些股票中每个股票的年度(日历年)股票价格回报。请注意,每种股票可能都没有相同的日期,因此,我尝试使用每种股票的最早和最新可用日期来动态计算收益。

我的桌子看起来像这样:

Date       | Stock    | Price
========== | ======== | =====
2018-01-03 | AAPL     | 200
2018-04-20 | AAPL     | 210
2018-07-10 | AAPL     | 230
2018-10-05 | AAPL     | 250
2018-12-20 | AAPL     | 290
2019-01-06 | AAPL     | 300
2019-06-15 | AAPL     | 280
2019-09-10 | AAPL     | 340
2019-12-28 | AAPL     | 400
2018-01-02 | MSFT     | 80
2018-04-20 | MSFT     | 90
2018-07-10 | MSFT     | 110
2018-10-05 | MSFT     | 100
2018-12-22 | MSFT     | 95
2019-01-10 | MSFT     | 110
2019-04-20 | MSFT     | 105
2019-06-19 | MSFT     | 120
2019-09-11 | MSFT     | 140
2019-12-30 | MSFT     | 150

我希望掌握每种股票的最早和最新股价,如下所示:

Date       | Stock    | Price
========== | ======== | =====
2018-01-03 | AAPL     | 200
2018-12-20 | AAPL     | 290
2019-01-06 | AAPL     | 300
2019-12-28 | AAPL     | 400
2018-01-02 | MSFT     | 80
2018-12-22 | MSFT     | 95
2019-01-10 | MSFT     | 110
2019-12-30 | MSFT     | 150

最后,我试图计算回报率(年末价格/年初价格-1)

Year  | Stock    | Return
===== | ======== | =====
2018  | AAPL     | 0.45
2019  | AAPL     | 0.3333
2018  | MSFT     | 0.1875
2019  | MSFT     | 0.3636

达到此结果的最有效方法是什么(因为我将在10年的时间内对1000余只股票进行计算,这很可能需要大量计算)?

解决方法

应该还不错。我已根据您的示例构建了此查询(在2017年添加了一行):

DECLARE @stocks TABLE (
    Date    DATETIME,Stock   VARCHAR(10),Price   MONEY
)

INSERT INTO  @stocks ( Date,Stock,Price )
VALUES
(' 2017-01-03','AAPL',200),(' 2018-01-03',(' 2018-04-20',210),(' 2018-07-10',230),(' 2018-10-05',250),(' 2018-12-20',290),(' 2019-01-06',300),(' 2019-06-15',280),(' 2019-09-10',340),(' 2019-12-28',400),(' 2018-01-02','MSFT',80 ),90 ),110),100),(' 2018-12-22',95 ),(' 2019-01-10',(' 2019-04-20',105),(' 2019-06-19',120),(' 2019-09-11',140),(' 2019-12-30',150)

SELECT S1.Stock,S1.MinDate,S2.Price,S1.MaxDate,S3.Price,(S3.Price / S2.Price) - 1 AS 'Return'
FROM (
    SELECT Stock,MIN(date) AS MinDate,MAX(date) AS MaxDate FROM @stocks GROUP BY Stock,YEAR(date)
) AS S1

LEFT JOIN @stocks AS S2
ON S2.Stock = S1.Stock
AND S2.Date = S1.MinDate

LEFT JOIN @stocks AS S3
ON S3.Stock = S1.Stock
AND S3.Date = S1.MaxDate

ORDER BY S1.Stock,YEAR(S1.MinDate)
,

鉴于您拥有过去10年的数据,则可以使用window function(最小,最大)尝试更快的查询。窗口函数根据一组行计算一个聚合值,并为每个组返回多个行。首先,使用window function获取最大和最小日期,然后使用WHERE过滤值,最后使用aggregate function获得此值的对应价格(不需要使用不同的):

--just get Price corresponding to min/max date grouping by Year,Stock
select Year,max(case when Date=max_date then Price end)/max(case when Date=min_date then Price end)-1 as [Return] from 
    (
    --get the MIN and MAX date partition by Year,Stock
    select *,min(Date)over(partition by Stock,datepart(yyyy,Date))min_date,max(Date)over(partition by Stock,Date))max_date,Date)Year from Table
    )X
   where min_date=Date or Date=max_date
   group by Stock,Year
,

一种无需子查询即可完成此操作的有趣方法是:

select distinct stock,year(date),first_value(price) over (partition by stock,year(date) order by date) as first_price,year(date) order by date desc) as last_price,(first_value(price) over (partition by stock,year(date) order by date desc) / 
        first_value(price) over (partition by stock,year(date) order by date) - 1
       ) as return
from t;

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...