【问题标题】:SQL Server Ranking issueSQL Server 排名问题
【发布时间】:2017-03-15 13:49:08
【问题描述】:

我正在尝试将排名应用于我的数据集,逻辑如下:

对于每个 ID,Order by ID2 ASC 和 Order by IsMaster Desc 对第 1 行进行排名,并且仅在 ID4 值更改时更改它

我的数据集和所需的输出如下所示:

测试数据

CREATE TABLE Test_Table 
(ID INT ,ID2 INT, IsMaster INT, ID4 VARCHAR(10))
GO

INSERT INTO Test_Table  (ID ,ID2 , IsMaster , ID4 )
VALUES 
 (1,    101,    1   ,'AAA')  -- 1  <-- Desired output for rank
,(1,    102,    0   ,'AAA')  -- 1
,(1,    103,    0   ,'AAB')  -- 2
,(1,    104,    0   ,'AAB')  -- 2
,(1,    105,    0   ,'CCC')  -- 3
,(2,    101,    1   ,'AAA')  -- 1
,(2,    102,    0   ,'AAA')  -- 1
,(2,    103,    0   ,'AAA')  -- 1
,(2,    104,    0   ,'AAB')  -- 2
,(2,    105,    0   ,'CCC')  -- 3

这是我迄今为止尝试过的:

SELECT *
    ,DENSE_RANK() OVER (PARTITION BY ID  ORDER BY ID2 ASC, IsMaster DESC ) rn
FROM Test_Table

请帮帮我,谢谢。

【问题讨论】:

    标签: sql-server sql-server-2014 ranking-functions


    【解决方案1】:

    这是一个孤岛/差距问题。

    • 首先您使用LAG() 来查看您在同一分区上是否有不同的ID4。
      • 重要的是你还需要partition by IsMaster
    • 然后在 ID4 更改时创建 islands
    • 最后使用可计算的SUM() 获得正确的排名。

    Sql Demo

    WITH id4_change as (
        SELECT  *, 
                LAG(ID4) OVER (PARTITION BY ID, IsMaster ORDER BY ID2) as prev
        FROM Test_Table
    ), islands as (
        SELECT *, 
               CASE WHEN ID4 = PREV 
                    THEN 0 
                    ELSE 1 
               END as island
        FROM id4_change
    ) 
    SELECT *,
           SUM(island) OVER (PARTITION BY ID, IsMaster ORDER BY ID2) rank
    FROM islands       
    ORDER BY ID, ID2, IsMaster DESC
    ;
    

    输出:你可以看到 ID4 = PREV 没有创建新的 "Island" 所以排名相同。

    编辑:您可以简化前两个查询

    WITH id4_change as (
        SELECT  *, 
                CASE WHEN ID4 = LAG(ID4) OVER (PARTITION BY ID, IsMaster ORDER BY ID2)
                     THEN 0
                     ELSE 1
                END as island
        FROM Test_Table
    ) 
    SELECT *,
           SUM(island) OVER (PARTITION BY ID, IsMaster ORDER BY ID2) rank
    FROM id4_change       
    ORDER BY ID, ID2, IsMaster DESC
    ;
    

    【讨论】:

    • @gordatron 是一个类似于ROW_NUMBER() 的窗口函数,也有LEAD() 来获取下一个值。
    • 谢谢@JuanCarlosOropeza,这非常接近,但如果 ID4 在第二行发生变化,它仍然显示相同的排名。例如,如果 ID 1 在第一个 AAA 值之后具有 AAB,则它会为第一行提供 1 的排名:( .
    • 你能用示例数据更新 rextexter 演示并分享新链接吗?那我可以看看吗?现在不理解示例。
    【解决方案2】:

    另一种方法可能效率较低,但它会起作用。

    WITH X AS
    (
     SELECT *
           ,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID2) RowNum
     FROM dbo.Test_Table
    )
    , CTE_VehicleNumber
    as
    (
        SELECT  T.ID , T.ID2, t.IsMaster ,T.ID4 , t.RowNum  , 1 as [Rank]
        FROM X  as T
        WHERE T.IsMaster = 1 
    
        UNION ALL
    
        SELECT  T.ID, T.ID2, t.IsMaster ,T.ID4 , t.RowNum , CASE WHEN t.ID4 <> c.ID4 THEN 1+ C.[Rank]
                                                                  ELSE 0+ C.[Rank]
                                                             END  as [Rank]
        FROM CTE_VehicleNumber as C
            inner join X  as T ON T.RowNum = C.RowNum + 1
                                                  AND t.ID = c.ID
    )
    SELECT ID , ID2, IsMaster ,ID4  , [Rank] 
    FROM CTE_VehicleNumber 
    ORDER BY ID , ID2, IsMaster ,ID4  , [Rank]
    OPTION (MAXRECURSION 0);
    

    【讨论】:

    • 非常感谢先生,这正是我想要的。再次非常感谢您。
    【解决方案3】:

    这应该会为您提供当前输入集所需的输出:

    SELECT *
        ,DENSE_RANK() OVER (PARTITION BY ID  ORDER BY ID4 ASC ) rn
    FROM Test_Table
    

    您的密集排名基于具有重复项的 ID4 列,而不是看似唯一的 ID2 列。但是,如果数据以不同的方式排序,这将不起作用,因此为了对此进行调整,您需要在最终语句中包含 ORDER BY 子句,如下所示:

    CREATE TABLE Test_Table 
    (ID INT ,ID2 INT, IsMaster INT, ID4 VARCHAR(10))
    GO
    
    INSERT INTO Test_Table  (ID ,ID2 , IsMaster , ID4 )
    VALUES 
     (1,    102,    0   ,'AAA')  -- 1
    ,(1,    103,    0   ,'AAB')  -- 2
    ,(1,    104,    0   ,'AAB')  -- 2
    ,(1,    105,    0   ,'CCC')  -- 3
    ,(2,    102,    0   ,'AAA')  -- 1
    ,(2,    103,    0   ,'AAA')  -- 1
    ,(2,    104,    0   ,'AAB')  -- 2
    ,(2,    105,    0   ,'CCC')  -- 3
    ,(1,    101,    1   ,'AAA')  -- 1  <-- Reordered inputs
    ,(2,    101,    1   ,'AAA')  -- 1
    
    SELECT *
        ,DENSE_RANK() OVER (PARTITION BY ID  ORDER BY ID4 ASC ) rn
    FROM Test_Table
    ORDER BY ID, rn, IsMaster DESC
    

    【讨论】:

    • 我认为这行不通。 OP 需要排名基于 ID4 的变化,但按 ID2 排序。
    • 只要 ID2 和 ID4 相关,这将起作用。我不知道数据,所以这是纯粹的猜测,没有来自 OP 的进一步澄清。
    • 您不会在任何地方考虑IsMaster,因此只需将 isMaster 分配给不同的记录,您就会得到不同的结果。 rextester.com/EQNI46744
    • @Juan 在外面的 ORDER BY 语句中。检查我的第二个脚本。
    • 查看 rextester 演示或查看我的答案。当 IsMaster 发生变化时,您需要重置排名。所以需要将它包含在分区中,而不仅仅是订购它。
    【解决方案4】:

    考虑到IDID4 中的其余数据,您确定您的ID2IsMaster 订单会影响预期结果吗?

    我只是尝试使用以下代码:

    ; WITH CTE AS (
        SELECT DISTINCT ID, ID4, DENSE_RANK() OVER (ORDER BY ID4) Rnk
        FROM #Test_Table
    )
    SELECT t.*, c.Rnk
    FROM #Test_Table t
        INNER JOIN CTE c ON t.ID = c.ID AND t.ID4 = c.ID4;
    

    ...即使更改 ID2IsMaster 的顺序,我也无法让它“行为不端” - 如果每组 @ 只有一个 IsMaster = 1 987654329@ 和ID2 中没有重复。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多