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
考虑到 ID
和 ID4
中的其余数据,您确定 ID2
和 IsMaster
的顺序会影响预期结果吗?
我刚刚尝试使用以下代码:
; 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;
...即使更改 ID2
和 IsMaster
的顺序,我也无法将其变为 "misbehave" - IF每组 ID4
中只有一个 IsMaster = 1
,并且 ID2
中没有重复项。
这是一个 island/gap 问题。
- 首先你用
LAG()
看看你在同一个分区上是否有不同的ID4。
- 很重要你还需要
partition by IsMaster
- 然后在 ID4 更改时创建
islands
。
- 最后使用计算
SUM()
获得正确的排名。
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);
我正在尝试将排名应用于我的数据集,逻辑如下:
对于每个 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
考虑到 ID
和 ID4
中的其余数据,您确定 ID2
和 IsMaster
的顺序会影响预期结果吗?
我刚刚尝试使用以下代码:
; 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;
...即使更改 ID2
和 IsMaster
的顺序,我也无法将其变为 "misbehave" - IF每组 ID4
中只有一个 IsMaster = 1
,并且 ID2
中没有重复项。
这是一个 island/gap 问题。
- 首先你用
LAG()
看看你在同一个分区上是否有不同的ID4。- 很重要你还需要
partition by IsMaster
- 很重要你还需要
- 然后在 ID4 更改时创建
islands
。 - 最后使用计算
SUM()
获得正确的排名。
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);