二维数据的复杂排名

时间:2016-03-30 15:24:50

标签: sql sql-server tsql

这是我正在处理的数据:

enter image description here

我想在sql中找到一种向黄色列添加数字的方法,它将按照以下方式对名称进行排名。

注意:这是最终的透视结果 - 在sql表格中无需透视数据。

enter image description here

此排名由以下规则决定:

  1. 最近一周(即Wk5专栏)是最重要的一周。
  2. 下一个最近的一周是下一个最重要的一周。
  3. ...左边是最早的一周栏" WK1"是最不重要的。
  4. 一个很小的数据值,例如1,最好。数据值很高,例如7,不好。空白是最差的,如果可能的话应该位于页面底部附近 - 但规则1/2/3始终优先。
  5. 这是Idx列中占位符为0的数据:

    CREATE TABLE #values 
        (
            Name    varchar(5), 
            Idx     int,
            "Week"  varchar(5), 
            Amount  int
        );
    
    INSERT INTO #values
    VALUES
        ('A',0,'WK1',3),
        ('T',0,'WK1',2),
        ('H',0,'WK1',1),
        ('P',0,'WK1',4),
        ('V',0,'WK1',6),
        ('N',0,'WK1',5),
        ('A',0,'WK2',2),
        ('F',0,'WK2',1),
        ('K',0,'WK2',3),
        ('P',0,'WK2',4),
        ('W',0,'WK2',7),
        ('V',0,'WK2',5),
        ('B',0,'WK2',6),
        ('A',0,'WK3',1),
        ('F',0,'WK3',2),
        ('T',0,'WK3',3),
        ('K',0,'WK3',4),
        ('W',0,'WK3',5),
        ('V',0,'WK3',6),
        ('N',0,'WK3',7),
        ('A',0,'WK4',2),
        ('F',0,'WK4',1),
        ('T',0,'WK4',5),
        ('K',0,'WK4',4),
        ('B',0,'WK4',6),
        ('A',0,'WK5',1),
        ('F',0,'WK5',2),
        ('T',0,'WK5',3),
        ('H',0,'WK5',4),
        ('K',0,'WK5',5);
    

    这是我目前的尝试:

    WITH  
    allData AS
        (
            SELECT  Name,
                    "Week",
                    newRank = RANK() OVER (ORDER BY "Week" DESC,Amount)
            FROM    #values
        )
    ,allData2 AS
        (
            SELECT  *,
                    newRank2 = 1 / CONVERT(NUMERIC(18,10),newRank)
            FROM    allData
        )
    ,allData3 AS
        (
            SELECT  Name,
                    smRank  = SUM(newRank2)
            FROM    allData2
            GROUP BY Name
        )
    SELECT  Name,
            smRank,
            rnk = RANK() OVER (ORDER BY smRank DESC)
    INTO    #RankA
    FROM    allData3;
    
    
    
    UPDATE      X
    SET         X.Idx = Y.rnk
    FROM        #values X
                INNER JOIN #RankA   Y ON
                    X.Name = Y.Name;
    

    不幸的是,如果我转动结果,然后按Idx列排序,则不符合我的目标。

2 个答案:

答案 0 :(得分:2)

使用#values表,以下是如何对其进行透视(因为您提供的数据不是相同的表格格式),然后根据您的要求为索引分配值。

select *
, ROW_NUMBER() OVER(ORDER BY CASE WHEN wk5 IS NULL THEN 1 ELSE 0 END, wk5, CASE WHEN wk4 IS NULL THEN 1 ELSE 0 END, wk4, CASE WHEN wk3 IS NULL THEN 1 ELSE 0 END,wk3, CASE WHEN wk2 IS NULL THEN 1 ELSE 0 END,wk2, CASE WHEN wk1 IS NULL THEN 1 ELSE 0 END, wk1) AS new_index

from (
select * from #values
) p
PIVOT (
MAX(Amount) 
FOR [week] IN (wk1, wk2, wk3, wk4, wk5)) AS pvt

动态使用52周

DECLARE @COLS AS NVARCHAR(MAX),
@QUERY  AS NVARCHAR(MAX)


SELECT @COLS = STUFF(( SELECT distinct ','+QUOTENAME(C.[week])
FROM #values AS C
FOR XML PATH('')), 1, 1, '')

SET @QUERY = '
select *
, ROW_NUMBER() OVER(ORDER BY CASE WHEN wk5 IS NULL THEN 1 ELSE 0 END, wk5, CASE WHEN wk4 IS NULL THEN 1 ELSE 0 END, wk4, CASE WHEN wk3 IS NULL THEN 1 ELSE 0 END,wk3, CASE WHEN wk2 IS NULL THEN 1 ELSE 0 END,wk2, CASE WHEN wk1 IS NULL THEN 1 ELSE 0 END, wk1) AS new_index

from (
select * from #values
) p
PIVOT (
MAX(Amount) 
FOR [week] IN (' + @cols+ ')) AS pvt'

EXEC(@QUERY)

答案 1 :(得分:2)

这是基于两个嵌套的ROW_NUMBER

select *,
   row_number() 
   over (order by "Week" desc, amount)
from
 (
   select *,
      row_number()
      over (partition by name 
            order by "Week" desc, amount) as rn
   from #values
 ) as dt
where rn = 1 -- for each name find the latest week and it's lowest number

如果两个名字共享同一周/金额怎么办?您可以考虑使用RANKDENSE_RANK