SQL 编号 - Row_Number() - 允许重复行号

Posted

技术标签:

【中文标题】SQL 编号 - Row_Number() - 允许重复行号【英文标题】:SQL Number - Row_Number() - Allow Repeating Row Number 【发布时间】:2015-10-26 08:07:46 【问题描述】:

我使用的是 SQL Server 2008。我在查询中返回了这些数据,该查询看起来很像按 Day 和 ManualOrder 排序的...

ID   Day  ManualOrder  Lat     Lon    
1    Mon  0            36.55   36.55  
5    Mon  1            55.55   54.44  
3    Mon  2            44.33   44.30  
10   Mon  3            36.55   36.55  
11   Mon  4            36.55   36.55  
6    Mon  5            20.22   22.11  
9    Mon  6            55.55   54.44  
10   Mon  7            88.99   11.22  
77   Sun  0            23.33   11.11  
77   Sun  1            23.33   11.11  

我要做的是让这些数据按 Day 排序,然后是 ManualOrder ......但我想要一个行计数器(我们称之为 MapPinNumber)。问题是我希望这个行计数器在同一天再次遇到相同的纬度/经度时重复。然后,如果它是不同的纬度/经度,它可以继续使用下一行的下一行计数器。我们必须在最终结果中维护 Day、ManualOrder 排序。

我将在地图上绘制这些,这个数字应该代表我将按 ManualOrder 顺序绘制的引脚号。该数据代表驾驶员的路线,他可能在一天中的日程安排中多次前往相同的纬度/经度。例如,他开车去沃尔玛,然后是 CVS,然后又回到沃尔玛,然后是沃尔格林。我需要的 MapPinNumber 列应该是 1、2、1、3。由于他周一多次去沃尔玛,但那也是他开车的第一个地方,所以它总是地图上的 Pin #1。

这是我需要计算的 MapPinNumber 列的结果。我已经用 ROW_NUMBER 和 RANK 尝试了所有我能想到的方法,结果发疯了!我试图避免使用丑陋的 CURSOR。

ID   Day  ManualOrder  Lat     Lon     MapPinNumber
1    Mon  0            36.55   36.55   1
5    Mon  1            55.55   54.44   2
3    Mon  2            44.33   44.30   3
10   Mon  3            36.55   36.55   1
11   Mon  4            36.55   36.55   1
6    Mon  5            20.22   22.11   4
9    Mon  6            55.55   54.44   2
10   Mon  7            88.99   11.22   5
77   Sun  0            23.33   11.11   1
77   Sun  1            23.33   11.11   1

【问题讨论】:

您应该使用RANK()DENSE_RANK()(视情况而定)而不是ROW_NUMBER() 我已经尝试过了,但似乎无法得到正确的结果。 我无法理解您的要求,能否请您具体说明一下您目前的结果和预期结果? 你上面显示的 MapPinNumber 是你想要的结果吗? 是的。我正在尝试根据其他列计算该列。最终结果必须按 Day ASC、ManualOrder ASC 排序。 【参考方案1】:

您可以使用聚合函数MINOVER 来创建您的排名组,然后DENSE_RANK 像这样处理它。

简要说明

    MIN(ManualOrder)OVER(PARTITION BY Day,Lat,Lon) 获得ManualOrder 的最小值DayLatLon

    DENSE_RANK() 只是将此值设置为来自1 的增量值。

SQL Fiddle

样本数据

CREATE TABLE Tbl ([ID] int, [Day] varchar(3), [ManualOrder] int, [Lat] int, [Lon] int);

INSERT INTO Tbl ([ID], [Day], [ManualOrder], [Lat], [Lon])
VALUES
    (1, 'Mon', 0, 36.55, 36.55),
    (5, 'Mon', 1, 55.55, 54.44),
    (3, 'Mon', 2, 44.33, 44.30),
    (10, 'Mon', 3, 36.55, 36.55),
    (11, 'Mon', 4, 36.55, 36.55),
    (6, 'Mon', 5, 20.22, 22.11),
    (9, 'Mon', 6, 55.55, 54.44),
    (10, 'Mon', 7, 88.99, 11.22),
    (77, 'Sun', 0, 23.33, 11.11),
    (77, 'Sun', 1, 23.33, 11.11);

查询

;WITH CTE AS 
(
SELECT *,GRP = MIN(ManualOrder)OVER(PARTITION BY Day,Lat,Lon) FROM Tbl
)
SELECT ID,Day,ManualOrder,Lat,Lon,DENSE_RANK()OVER(PARTITION BY Day ORDER BY GRP) AS RN
FROM CTE
ORDER BY Day,ManualOrder

输出

ID  Day ManualOrder Lat Lon RN
1   Mon 0   36.55   36.55   1
5   Mon 1   55.55   54.44   2
3   Mon 2   44.33   44.30   3
10  Mon 3   36.55   36.55   1
11  Mon 4   36.55   36.55   1
6   Mon 5   20.22   22.11   4
9   Mon 6   55.55   54.44   2
10  Mon 7   88.99   11.22   5
77  Sun 0   23.33   11.11   1
77  Sun 1   23.33   11.11   1

【讨论】:

非常感谢。您的回答很详细并进行了解释,并且您提供了一个简短的解决方案。你帮我减轻了很多压力!!它运行良好。【参考方案2】:

这将为您提供所需的结果。但是,MapPinNumber 的顺序可能与结果中显示的顺序不同。

SELECT *, 
    MapPinNumber = DENSE_RANK() OVER (PARTITION BY Day ORDER BY Lat, Lon) 
FROM Table1
ORDER BY Day, ManualOrder

【讨论】:

如果它的顺序不同,那么它显然不适用于 OP 的既定目的(绘制路线)。【参考方案3】:

这可能不是最优雅的解决方案,但它确实有效:

Select a.*, b.MapPinOrder from MyTable a
left join
  (
  select distinct Day, Lat, Lon
    , row_number() 
      over (partition by Day order by min(ManualOrder)) as MapPinOrder
  from MyTable
  group by Day, Lat, Lon
  ) b
on a.day = b.day 
  and a.lat = b.lat 
  and a.lon = b.lon  

使用您想要的顺序分别计算行,然后将它们重新连接到整个表中。

SQL Fiddle

【讨论】:

【参考方案4】:

这是我使用ROW_NUMBER的尝试:

SQL Fiddle

WITH CteRN AS(
    SELECT *,
        Rn = ROW_NUMBER() OVER(PARTITION BY Day ORDER BY ManualOrder),
        Grp = ROW_NUMBER() OVER(PARTITION BY Day, Lat, Lon ORDER BY ManualOrder)
    FROM tbl
),
CteBase AS(
    SELECT *,
        N = ROW_NUMBER() OVER(PARTITION BY Day ORDER BY ManualOrder)
    FROM CteRN
    WHERE Grp = 1
)
SELECT 
    r.ID, r.Day, r.ManualOrder, r.Lat, r.Lon,
    MapPinNumber = ISNULL(b.N, r.RN)
FROM CteRN r
LEFT JOIN CteBase b
    ON b.Day = r.Day
    AND b.Lat = r.Lat
    AND b.Lon = r.Lon
ORDER BY 
    r.Day, r.ManualOrder

【讨论】:

以上是关于SQL 编号 - Row_Number() - 允许重复行号的主要内容,如果未能解决你的问题,请参考以下文章

数据仓库系列 之SQL中row_number() over (partition by)的详解

T-SQL - 我可以强制 ROW_NUMBER() OVER (PARTITION BY... 从设定点对记录进行编号吗?

SQL中隔行编号的操作

SQL Server之排序函数

SQL Server之排序函数

SQL Server 分页编号的另一种方式