使用 CTE 的递归在非连续的几天内不起作用,但使用 ROW_NUMBER 时非常慢

Recursion with CTE not working for non-consecutive days but very slow with ROW_NUMBER

我必须计算股票价格的指数移动平均线。在查询中,我根据 PriceDate 进行了递归。但这实际上不起作用,因为缺少天数(周末、节假日)。我试过用 ROW_NUMBER 来做,但是 运行 真的很慢(在我取消之前超过 40 分钟)。

我正在寻找一种方法来处理日期,但要考虑到缺少日期。或者如果它是 ROW_NUMBER,我将需要认真的速度优化:

DECLARE @timePeriod12 INT = 12
DECLARE @smoothingFactor12 FLOAT = 2.0/(@timePeriod12 + 1);


;WITH SMA AS
(
  -- calculate SMA for each row for the last N days
  SELECT @smoothingFactor12 as alpha
      --  , ROW_NUMBER() OVER (PARTITION BY Ticker ORDER BY PriceDate DESC) rownum
       , Ticker
       , PriceDate
       , ClosePrice
       , AVG(ClosePrice) OVER (PARTITION BY Ticker ORDER BY PriceDate ROWS BETWEEN 11 PRECEDING AND CURRENT ROW) AS sma
       
  FROM price.PriceHist
  WHERE PriceDate > (SELECT MAX(PriceDate) - 40 as PriceDate FROM price.PriceHist) --AND Ticker = 'AAPL'
  
),

EMA AS
(
    SELECT Ticker, PriceDate, ClosePrice, CONVERT(DECIMAL(10, 4), sma) AS ema 
      FROM SMA
            

     UNION ALL

     SELECT curr.Ticker, curr.PriceDate, curr.ClosePrice, CONVERT(DECIMAL(10,4), calc.ema) AS EMA 
       FROM EMA previous
      INNER
       JOIN SMA curr
         ON curr.PriceDate = previous.PriceDate + 1
        AND curr.Ticker = previous.Ticker
      CROSS
      APPLY (SELECT curr.alpha * curr.ClosePrice + (1 - curr.alpha) *  previous.ema AS ema) calc
      
)

INSERT INTO #tempEMA(Ticker, PriceDate, ClosePrice, EMA12)
SELECT * FROM EMA  

OPTION (MAXRECURSION 0)

GO

上面的查询结果清楚地表明,按照我的方式使用 PriceDate 是非常错误的。

我听取了@Alex 的建议,将 SMA 计算单独移动 table。效果很好。

IF OBJECT_ID('tempdb..#tempSMA') IS NOT NULL DROP TABLE #tempSMA

CREATE TABLE #tempSMA
(
Ticker VARCHAR(20), 
PriceDate DATETIME2, 
ClosePrice DECIMAL(17,5), 
sma DECIMAL(17,5),
rownum INT,     
);

INSERT INTO #tempSMA(Ticker, PriceDate, ClosePrice, sma, rownum)
SELECT
         Ticker
       , PriceDate
       , ClosePrice
       , AVG(ClosePrice) OVER (PARTITION BY Ticker ORDER BY PriceDate ROWS BETWEEN 26 PRECEDING AND CURRENT ROW) AS sma 
       , ROW_NUMBER() OVER (PARTITION BY Ticker ORDER BY PriceDate) rownum
    FROM price.PriceHist
    WHERE PriceDate > (SELECT MAX(PriceDate) - 40 as PriceDate FROM price.PriceHist)

DECLARE @timePeriod12 INT = 12
DECLARE @smoothingFactor12 FLOAT = 2.0/(@timePeriod12 + 1);

;WITH SMA AS
(
  -- calculate SMA for each row for the last N days
  SELECT @smoothingFactor12 as alpha
       , rownum
       , Ticker
       , PriceDate
       , ClosePrice
       , sma
       
  FROM #tempSMA
  
),

EMA AS
(
    SELECT Ticker, PriceDate, ClosePrice, CONVERT(DECIMAL(10, 4), sma) AS ema, rownum 
    FROM SMA
    WHERE rownum = (SELECT MAX(rownum) / 2 FROM SMA)       

     UNION ALL

     SELECT curr.Ticker, curr.PriceDate, curr.ClosePrice, CONVERT(DECIMAL(10,4), calc.ema) AS EMA, curr.rownum 
       FROM EMA previous
      INNER
       JOIN SMA curr
         ON curr.rownum = previous.rownum + 1
        AND curr.Ticker = previous.Ticker
      CROSS
      APPLY (SELECT curr.alpha * curr.ClosePrice + (1 - curr.alpha) *  previous.ema AS ema) calc
      
)

INSERT INTO #tempEMA(Ticker, PriceDate, ClosePrice, EMA12)
SELECT Ticker, PRiceDate, ClosePrice, ema FROM EMA  

OPTION (MAXRECURSION 0)

GO