zoukankan      html  css  js  c++  java
  • 排名趋势公式

    开始

    这里有个连续几个月的个人销售数据,如:

                图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

    最后

      这只是一个简单的例子,但可以从简单的例子发现一些事物的某种共性,由此可以明白它可以应用于相识而不同的各种应用案例中。

  • 相关阅读:
    java初学者之java语言主要知识点三
    C++类的对象和类的指针的区别
    win32多线程: 线程创建与结束等待
    多线程学习:win32多线程编程基本概念(转)
    C++常用数据类型和Windows常见数据类型
    VC++2017关于项目出现"const char *" 类型的实参与 "char *" 类型的形参不兼容错误的解决方法
    Linux环境下vi/vim编辑器常用命令
    c++学习笔记之类模板
    c++学习笔记之函数重载和模板理解
    c++学习笔记之多态和虚函数
  • 原文地址:https://www.cnblogs.com/wghao/p/6830578.html
Copyright © 2011-2022 走看看