【问题标题】:Calculating returns within SQL query在 SQL 查询中计算返回值
【发布时间】:2020-12-19 22:52:27
【问题描述】:

我有多家公司过去 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 多只股票进行此操作,这可能需要大量计算)?

【问题讨论】:

  • 使用row_number partition by (year, stock) 获得第一个结果
  • 请向我们展示您的尝试。

标签: sql sql-server stock


【解决方案1】:

应该不会太糟糕。我根据您的示例构建了此查询(加上 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' , '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)

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)

【讨论】:

    【解决方案2】:

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

    --just get Price corresponding to min/max date grouping by Year,Stock
    select Year,Stock,  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,datepart(yyyy,Date))max_date,
        datepart(yyyy,Date)Year from Table
        )X
       where min_date=Date or Date=max_date
       group by Stock,Year
    

    【讨论】:

      【解决方案3】:

      不使用子查询的有趣方法是:

      select distinct stock, year(date),
             first_value(price) over (partition by stock, year(date) order by date) as first_price,
             first_value(price) over (partition by stock, 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;
      

      【讨论】:

      • 语法很酷,但是当您查看执行计划时,您会发现它是迄今为止提出的 3 个解决方案中最差的。
      • @emoreau99 。 . .你会注意到我并没有声称这是最快的方法。
      • 我最喜欢 Gordon 的解决方案。它又紧又脆。
      猜你喜欢
      • 2013-06-11
      • 1970-01-01
      • 2018-07-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-28
      • 2022-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多