使用 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
我必须计算股票价格的指数移动平均线。在查询中,我根据 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