SQL 服务器排名
SQL Server Ranking
我有 table 只股票,我想为每只股票创建滚动排名。
这是我的数据集示例:
StockID QuoteID QuoteDay QuoteClose
47 230 2018-04-06 5.1200
47 231 2018-04-07 5.2100
47 232 2018-04-08 5.3000
47 233 2018-04-09 5.2100
47 234 2018-04-10 5.1900
47 235 2018-04-11 5.5200
47 236 2018-04-12 7.1600
47 237 2018-04-13 6.6900
47 238 2018-04-14 6.6300
47 239 2018-04-15 7.0200
47 240 2018-04-16 6.6300
47 241 2018-04-17 6.5800
251 100 2018-04-06 0.1906
251 101 2018-04-07 0.1969
251 102 2018-04-08 0.1986
251 103 2018-04-09 0.2291
251 104 2018-04-10 0.1963
251 105 2018-04-11 0.1995
251 106 2018-04-12 0.2271
251 107 2018-04-13 0.3722
251 108 2018-04-14 0.3073
251 109 2018-04-15 0.3292
251 110 2018-04-16 0.2905
251 111 2018-04-17 0.2784
每一天,我都想对前 6 天的收盘价进行排名。
例如:
StockID QuoteID QuoteDay QuoteClose 7d Rank
47 230 04/06/18 5.1200
47 231 04/07/18 5.2100
47 232 04/08/18 5.3000
47 233 04/09/18 5.2100
47 234 04/10/18 5.1900
47 235 04/11/18 5.5200
47 236 04/12/18 7.1600 1
47 237 04/13/18 6.6900 2
47 238 04/14/18 6.6300 3
47 239 04/15/18 7.0200 2
47 240 04/16/18 6.6300 4
47 241 04/17/18 6.5800 6
251 100 04/06/18 0.1906
251 101 04/07/18 0.1969
251 102 04/08/18 0.1986
251 103 04/09/18 0.2291
251 104 04/10/18 0.1963
251 105 04/11/18 0.1995
251 106 04/12/18 0.2271 2
251 107 04/13/18 0.3722 1
251 108 04/14/18 0.3073 2
251 109 04/15/18 0.3292 2
251 110 04/16/18 0.2905 4
251 111 04/17/18 0.2784 5
请注意,前 6 天没有排名。
我在将排名与行号结合起来时遇到问题,所以我的方法是使用一个循环,一次只为每只股票提供 7 天的数据。
例如,对于 2018 年 4 月 12 日的股票 #47,我正在比较 4/6/8-4/12/8 的 Quoteclose 以确定排名为 1。
然后我将该排名保存到另一个 table。
循环然后将 window 向前移动 1 天,并找到 2018 年 4 月 13 日的排名,这将是 2。对这只股票重复该过程,然后对 table.
我的计划很复杂,但应该能完成。
是否有更好更简洁的方法来实现所有股票的滚动排名?
我正在努力使它成为一个动态过程,以便我可以生成不同的排名周期:7 天、20 天等。
我不确定我是否理解你的排名,但我假设它是从最好(最大)到最差(最小)排名的 7(可变天)滚动平均值
declare @t table (
StockID int, QuoteID int, QuoteDay date, QuoteClose money)
insert into @t
values
(47,230,'20180406',5.12)
, (47,231,'20180407',5.21)
, (47,232,'20180408',5.3)
, (47,233,'20180409',5.21)
, (47,234,'20180410',5.19)
, (47,235,'20180411',5.52)
, (47,236,'20180412',7.16)
, (47,237,'20180413',6.69)
, (47,238,'20180414',6.63)
, (47,239,'20180415',7.02)
, (47,240,'20180416',6.63)
, (47,241,'20180417',6.58)
, (251,100,'20180406',0.1906)
, (251,101,'20180407',0.1969)
, (251,102,'20180408',0.1986)
, (251,103,'20180409',0.2291)
, (251,104,'20180410',0.1963)
, (251,105,'20180411',0.1995)
, (251,106,'20180412',0.2271)
, (251,107,'20180413',0.3722)
, (251,108,'20180414',0.3073)
, (251,109,'20180415',0.3292)
, (251,110,'20180416',0.2905)
, (251,111,'20180417',0.2784)
declare @dayInRank int = 7
;with cte as
(
select rn=row_number()over(partition by StockID order by QuoteDay)
,*
from @t
)
,RollingAvg as
(
select StockID,QuoteDay,RollingAvg=s.RollingSum/@dayInRank
from cte
cross apply (select SUM(QuoteClose) from cte c2 where cte.StockID=c2.StockID and c2.rn between cte.rn-(@dayInRank-1) and cte.rn) s(RollingSum)
where rn>=@dayInRank
)
select *,Rank=Row_Number()over (partition by StockID order by ra.RollingAvg desc)
from RollingAvg ra
order by StockID,QuoteDay
将其分解为以下步骤:
首先 (OrderedRankSets CTE) 为每个 StockID 和 QuoteDay 获取一组行,其中包括当前行和所有先前行,按 QuoteDay 降序编号。我们稍后可以使用此编号来限制我们要排名的先前行。
其次(RankedQuoteDays CTE),对每个报价集进行排名,我们实际上至少拥有每个报价日期间内的行数,并且只包括向上的行到周期长度。
最后,我们不需要输出集合的所有行,我们只需要每个报价日(报价日 = 之前的报价日)的最新行,所以最终查询只是转储(见 SQL Fiddle)
DECLARE @Period INT = 7;
WITH
OrderedRankSets AS (
SELECT
Quotes.StockID
, Quotes.QuoteID
, Quotes.QuoteDay
, PriorQuotes.QuoteDay AS PriorQuoteDay
, PriorQuotes.QuoteClose
, ROW_NUMBER() OVER (PARTITION BY Quotes.StockID, Quotes.QuoteDay ORDER BY PriorQuotes.QuoteDay DESC) AS RowNumber
, COUNT(*) OVER (PARTITION BY Quotes.StockID, Quotes.QuoteDay) AS [RowCount]
FROM
Quotes
JOIN Quotes AS PriorQuotes ON (PriorQuotes.StockID = Quotes.StockID
AND PriorQuotes.QuoteDay <= Quotes.QuoteDay)
)
, RankedQuoteDays AS (
SELECT
OrderedRankSets.*
, CASE WHEN [RowCount] < @Period THEN NULL ELSE RANK() OVER (PARTITION BY StockID, QuoteDay ORDER BY QuoteClose DESC) END AS QuoteRank
FROM
OrderedRankSets
WHERE
RowNumber <= @Period
)
SELECT
RankedQuoteDays.StockID
, RankedQuoteDays.QuoteID
, RankedQuoteDays.QuoteDay
, RankedQuoteDays.QuoteClose
, RankedQuoteDays.QuoteRank
FROM
RankedQuoteDays
WHERE
QuoteDay = PriorQuoteDay
ORDER BY
StockID, QuoteDay
我有 table 只股票,我想为每只股票创建滚动排名。
这是我的数据集示例:
StockID QuoteID QuoteDay QuoteClose
47 230 2018-04-06 5.1200
47 231 2018-04-07 5.2100
47 232 2018-04-08 5.3000
47 233 2018-04-09 5.2100
47 234 2018-04-10 5.1900
47 235 2018-04-11 5.5200
47 236 2018-04-12 7.1600
47 237 2018-04-13 6.6900
47 238 2018-04-14 6.6300
47 239 2018-04-15 7.0200
47 240 2018-04-16 6.6300
47 241 2018-04-17 6.5800
251 100 2018-04-06 0.1906
251 101 2018-04-07 0.1969
251 102 2018-04-08 0.1986
251 103 2018-04-09 0.2291
251 104 2018-04-10 0.1963
251 105 2018-04-11 0.1995
251 106 2018-04-12 0.2271
251 107 2018-04-13 0.3722
251 108 2018-04-14 0.3073
251 109 2018-04-15 0.3292
251 110 2018-04-16 0.2905
251 111 2018-04-17 0.2784
每一天,我都想对前 6 天的收盘价进行排名。
例如:
StockID QuoteID QuoteDay QuoteClose 7d Rank
47 230 04/06/18 5.1200
47 231 04/07/18 5.2100
47 232 04/08/18 5.3000
47 233 04/09/18 5.2100
47 234 04/10/18 5.1900
47 235 04/11/18 5.5200
47 236 04/12/18 7.1600 1
47 237 04/13/18 6.6900 2
47 238 04/14/18 6.6300 3
47 239 04/15/18 7.0200 2
47 240 04/16/18 6.6300 4
47 241 04/17/18 6.5800 6
251 100 04/06/18 0.1906
251 101 04/07/18 0.1969
251 102 04/08/18 0.1986
251 103 04/09/18 0.2291
251 104 04/10/18 0.1963
251 105 04/11/18 0.1995
251 106 04/12/18 0.2271 2
251 107 04/13/18 0.3722 1
251 108 04/14/18 0.3073 2
251 109 04/15/18 0.3292 2
251 110 04/16/18 0.2905 4
251 111 04/17/18 0.2784 5
请注意,前 6 天没有排名。
我在将排名与行号结合起来时遇到问题,所以我的方法是使用一个循环,一次只为每只股票提供 7 天的数据。
例如,对于 2018 年 4 月 12 日的股票 #47,我正在比较 4/6/8-4/12/8 的 Quoteclose 以确定排名为 1。
然后我将该排名保存到另一个 table。
循环然后将 window 向前移动 1 天,并找到 2018 年 4 月 13 日的排名,这将是 2。对这只股票重复该过程,然后对 table.
我的计划很复杂,但应该能完成。
是否有更好更简洁的方法来实现所有股票的滚动排名?
我正在努力使它成为一个动态过程,以便我可以生成不同的排名周期:7 天、20 天等。
我不确定我是否理解你的排名,但我假设它是从最好(最大)到最差(最小)排名的 7(可变天)滚动平均值
declare @t table (
StockID int, QuoteID int, QuoteDay date, QuoteClose money)
insert into @t
values
(47,230,'20180406',5.12)
, (47,231,'20180407',5.21)
, (47,232,'20180408',5.3)
, (47,233,'20180409',5.21)
, (47,234,'20180410',5.19)
, (47,235,'20180411',5.52)
, (47,236,'20180412',7.16)
, (47,237,'20180413',6.69)
, (47,238,'20180414',6.63)
, (47,239,'20180415',7.02)
, (47,240,'20180416',6.63)
, (47,241,'20180417',6.58)
, (251,100,'20180406',0.1906)
, (251,101,'20180407',0.1969)
, (251,102,'20180408',0.1986)
, (251,103,'20180409',0.2291)
, (251,104,'20180410',0.1963)
, (251,105,'20180411',0.1995)
, (251,106,'20180412',0.2271)
, (251,107,'20180413',0.3722)
, (251,108,'20180414',0.3073)
, (251,109,'20180415',0.3292)
, (251,110,'20180416',0.2905)
, (251,111,'20180417',0.2784)
declare @dayInRank int = 7
;with cte as
(
select rn=row_number()over(partition by StockID order by QuoteDay)
,*
from @t
)
,RollingAvg as
(
select StockID,QuoteDay,RollingAvg=s.RollingSum/@dayInRank
from cte
cross apply (select SUM(QuoteClose) from cte c2 where cte.StockID=c2.StockID and c2.rn between cte.rn-(@dayInRank-1) and cte.rn) s(RollingSum)
where rn>=@dayInRank
)
select *,Rank=Row_Number()over (partition by StockID order by ra.RollingAvg desc)
from RollingAvg ra
order by StockID,QuoteDay
将其分解为以下步骤:
首先 (OrderedRankSets CTE) 为每个 StockID 和 QuoteDay 获取一组行,其中包括当前行和所有先前行,按 QuoteDay 降序编号。我们稍后可以使用此编号来限制我们要排名的先前行。
其次(RankedQuoteDays CTE),对每个报价集进行排名,我们实际上至少拥有每个报价日期间内的行数,并且只包括向上的行到周期长度。
最后,我们不需要输出集合的所有行,我们只需要每个报价日(报价日 = 之前的报价日)的最新行,所以最终查询只是转储(见 SQL Fiddle)
DECLARE @Period INT = 7;
WITH
OrderedRankSets AS (
SELECT
Quotes.StockID
, Quotes.QuoteID
, Quotes.QuoteDay
, PriorQuotes.QuoteDay AS PriorQuoteDay
, PriorQuotes.QuoteClose
, ROW_NUMBER() OVER (PARTITION BY Quotes.StockID, Quotes.QuoteDay ORDER BY PriorQuotes.QuoteDay DESC) AS RowNumber
, COUNT(*) OVER (PARTITION BY Quotes.StockID, Quotes.QuoteDay) AS [RowCount]
FROM
Quotes
JOIN Quotes AS PriorQuotes ON (PriorQuotes.StockID = Quotes.StockID
AND PriorQuotes.QuoteDay <= Quotes.QuoteDay)
)
, RankedQuoteDays AS (
SELECT
OrderedRankSets.*
, CASE WHEN [RowCount] < @Period THEN NULL ELSE RANK() OVER (PARTITION BY StockID, QuoteDay ORDER BY QuoteClose DESC) END AS QuoteRank
FROM
OrderedRankSets
WHERE
RowNumber <= @Period
)
SELECT
RankedQuoteDays.StockID
, RankedQuoteDays.QuoteID
, RankedQuoteDays.QuoteDay
, RankedQuoteDays.QuoteClose
, RankedQuoteDays.QuoteRank
FROM
RankedQuoteDays
WHERE
QuoteDay = PriorQuoteDay
ORDER BY
StockID, QuoteDay