這隻是一個簡單的排名例子,但可以從簡單的例子發現一些事物的某種共性,由此可以明白它可以應用於相識而不同的各種應用案例中。 ...
開始
這裡有個連續幾個月的個人銷售數據,如:
圖1.
要求對個人銷量按月進行排名,還能夠描述出一個人在連續幾個月的排名趨勢,是上升還是下降,連續上升多少個月,連續下降多少個月,如圖2.
圖2.
分析
由於要描述連續升和連續降的特點,每月的連續升降,需要參考本月之前的排名情況,所以最好是通過實體表來存儲每月的排名數據。如圖3.
圖3.
在 圖3,中,有一個欄位RankTrend描述排名趨勢,正數(+n)表示上升,負數(-n)表示下降,0表示第一次排名或名次不變。
場景1:第一次排名從0開始計數,下個月名次上升的情況,RankTrend +=1, 反之,名次出現下降 ,RankTrend -=1 。
圖4.
Kent 在1月份第一次加入排名,1月排名趨勢為0;2月份,名次從第4名升到第2名,屬於上升趨勢,那麼RankTrend +=1; 3月也是一樣,從2月的第2名升到第1名,那麼RankTrend +=1;
圖5.
John 在1月份第一次加入排名,1月排名趨勢為0;2月份,名次從第1名降到第5名,屬於下降趨勢,那麼RankTrend -=1; 3月也是一樣,從2月的第5名降到第6名,那麼RankTrend -=1;
場景2:上個月的排名趨勢是上升的,即RankTrend = (+n), 本月排名突然下降,那麼RankTrend = (-1),這裡不能直接RankTrend -=1,因為直接減1不能體現連續升降趨勢。
圖6.
分析方法如前面(略.)
場景3:上個月的排名趨勢是下降的,即RankTrend = (-n), 本月排名突然下降,那麼RankTrend = (+1),這裡不能直接RankTrend +=1,因為直接加1不能體現連續升降趨勢。
圖7.
分析方法如前面(略.)
排名趨勢公式
根據前面的分析,我們涉及到一些數值,(+n),(-n) ,-1 , 1 , 0 。結合這些數,我們可以通過一個簡單的數字組合和位運算,整理得出一個排名趨勢公式:
【排名趨勢】 = isnull( sign( [上月名次] - [本月名次] ) + [上月排名趨勢值] x ( 1 - abs(sign( sign([上月名次] - [本月名次]) XOR sign([上月排名趨勢值]) )) ) ,0)
[註明]:
- isnull()函數是判斷Null的時候取0
- sign() 函數是正負函數,返回-1,0,1
- abs() 函數是取絕對值
- XOR 表示異或運算
具體可以自己去拆分,這裡暫時不詳解。
測試例子
1.建表和Insert 測試數據
use tempdb go if object_id('PersonalSalesRank') Is not null drop Table PersonalSalesRank go create table PersonalSalesRank(Name nvarchar(20),YM date, SalesQty int ,RankNr int,RankTrend int) go set nocount on insert into dbo.PersonalSalesRank ( Name , YM , SalesQty ) values --2017-01 ('Kent','20170101',400), ('John','20170101',758), ('Rob','20170101',365), ('Ruben','20170101',487), ('Andy','20170101',651), --2017-02 ('Andy','20170201',668), ('Christy','20170201',541), ('Kent','20170201',712), ('Ruben','20170201',729), ('Rob','20170201',365), ('John','20170201',465), --2017-03 ('Andy','20170301',651), ('Christy','20170301',588), ('Kent','20170301',769), ('Ruben','20170301',752), ('Rob','20170301',552), ('John','20170301',421) go
2.創建排名趨勢函數
if object_id('fn_RankTrend') Is not null drop function fn_RankTrend go /******************************************************************************************** %% Program Name :fn_RankTrend %% Author : Andy.Wei %% Description : %% InParameter : %% OutParameter : %% Created Date : 2017-05-12 %% Revision History : %% Modified Date Modified By version Description %% ********************************************************************************************/ create function fn_RankTrend ( @CurrentRankNr int, @LastRankNr int, @LastRankTrend int ) returns int as begin return(isnull(sign( @LastRankNr - @CurrentRankNr) + @LastRankTrend * ( 1 - abs(sign( sign(@LastRankNr - @CurrentRankNr)^sign(@LastRankTrend) )) ) ,0)) end go
3. 計算排名
--排名 ;with cte_Rank as ( select RankNr,dense_rank() over(partition by YM order by SalesQty desc) as RankNr_1 from dbo.PersonalSalesRank ) update a set a.RankNr=a.RankNr_1 from cte_Rank a
4. 計算排名趨勢
--更新排名趨勢 declare @YM date='20170101' while(1=1) begin update a set a.RankTrend=dbo.fn_RankTrend(a.RankNr,b.RankNr,b.RankTrend) from dbo.PersonalSalesRank a left join dbo.PersonalSalesRank b on b.Name=a.Name and b.YM=dateadd(month,-1,@YM) where a.YM=@YM; if @@ROWCOUNT =0 break; print @YM set @YM=dateadd(month,1,@YM) end
5. 測試
select name as [姓名], convert(char(7),a.YM,121) as [月份], a.SalesQty as [銷量], convert(nvarchar(20),N'第 '+rtrim(a.RankNr)+N' 名') as [排名], case sign(a.RankTrend) when -1 then N'↓' when 1 then N'↑' else N'' end as [排名升降], case when a.RankTrend>=2 then N'連續'+rtrim(abs(a.RankTrend))+N'月' else N'' end as [連續上升], case when a.RankTrend<=-2 then N'連續'+rtrim(abs(a.RankTrend))+N'月' else N'' end as [連續下降] from dbo.PersonalSalesRank a order by 2,3 desc go
最後
這隻是一個簡單的例子,但可以從簡單的例子發現一些事物的某種共性,由此可以明白它可以應用於相識而不同的各種應用案例中。