SQL 服务器排名问题

SQL Server Ranking issue

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

对于每个 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

请帮助我谢谢。

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

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

考虑到 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 的顺序,我也无法将其变为 "misbehave" - IF每组 ID4 中只有一个 IsMaster = 1,并且 ID2 中没有重复项。

这是一个 island/gap 问题。

  • 首先你用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
;

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

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);