SQL 对于所有公司都没有收盘价的日期

SQL for Dates with no ClosePrice for all companies

我有一个事实 table 我有公司代码和交易日期的收盘价。我还有一个 dimCalendar table,其中所有日期都在那里,并且为交易假期保留了一个标志。

不知何故,我需要有一个新的事实 table 或物化视图或其他用于报告目的的东西,我需要每天都有 ClosePrice,包括 TradingHolidays。

交易假期的收盘价应该是该 CompanyTicker 的前一个交易日的收盘价。然后我需要有另一列“5 天平均收盘价”。计算平均收盘价只应包括交易日。

所以,让我们假设这是当前状态。State1

下面是加入日历后的情况。

我想第一步是左加入日历 Table。这给了我 0 天的股票价格。

select a.date as tdate, a.datekey, b.ticker, coalesce(b.ClosePrice,0)
from dimdates a left join 
    factStockDividendCommodity b
    on a.DateKey = b.datekey --and b.ticker = 'BP'
where (a.Datekey between 20180101 and 20181231 )
order by a.Date

不确定如何获得代码和前一个交易日的收盘价。

关键思想是使用 cross join 生成行,然后填写值。在您的情况下,您可能想要考虑到库存在过去的所有时间点都可能不存在,因此您只希望在最短观察日期内使用它。

要填写日期,可以在标准SQL中使用lag(ignore nulls):

select d.date as tdate, d.datekey, t.ticker, 
       coalesce(fsdc.ClosePrice,
                lag(fsdc.ClosePrice ignore nulls) over (partition by t.ticker order by d.date) as ClosePrice
from dimdates d join
     (select ticker, min(datekey) as min_date
      from factStockDividendCommodity fsdc
      group by ticker
     ) t
     on d.datekey >= t.min_datekey left join
     factStockDividendCommodity fsdc
     on fsdc.ticker = t.ticker and
        fsdc.datekey = d.datekey
where d.Datekey between 20180101 and 20181231
order by d.Date;

唉,许多数据库——甚至那些支持 lag() 的数据库——都不支持 ignore nulls 选项。那么最好的方法取决于数据库。相关子查询是最通用的方法,但从性能的角度来看可能不是最好的方法。

编辑:

SQL 服务器不支持 IGNORE NULLS 选项。 使用 OUTER APPLY:

可能最容易处理
select d.date as tdate, d.datekey, t.ticker, 
       fsdc.ClosePrice as ClosePrice
from dimdates d join
     (select ticker, min(datekey) as min_date
      from factStockDividendCommodity fsdc
      group by ticker
     ) t
     on d.datekey >= t.min_datekey outer apply
     (select top (1) fsdc.*
      from factStockDividendCommodity fsdc
      where fsdc.ticker = t.ticker and
            fsdc.datekey <= d.datekey
      order by fsdc.datekey desc
     ) fsdc
where d.Datekey between 20180101 and 20181231
order by d.Date;

但是,因为连续没有值的时间可能永远不会超过 3 或 4 天,所以一系列 lag() 可能更有效:

select d.date as tdate, d.datekey, t.ticker, 
       coalesce(fsdc.ClosePrice,
                lag(fsdc.ClosePrice, 1) over (partition by t.ticker order by d.date),
                lag(fsdc.ClosePrice, 2) over (partition by t.ticker order by d.date),
                lag(fsdc.ClosePrice, 3) over (partition by t.ticker order by d.date)
               ) as ClosePrice
from dimdates d join
     (select ticker, min(datekey) as min_date
      from factStockDividendCommodity fsdc
      group by ticker
     ) t
     on d.datekey >= t.min_datekey left join
     factStockDividendCommodity fsdc
     on fsdc.ticker = t.ticker and
        fsdc.datekey = d.datekey
where d.Datekey between 20180101 and 20181231
order by d.Date;