DENSE_RANK 更改时按顺序中断
DENSE_RANK breaking sequentially when changed
grp
列应表示由 GroupId, RouteId
分区并排序 LengthStart
的块,如果 locationId 保持不变,则当您穿过块时 grp
是一样的。序列中断会创建一个新的 grp
+---------+----------+--------------+------------+-------------+------+
| GROUPID | ROUTEID | LENGTHSTART | LENGTHEND | LOCATIONID | GRP |
+---------+----------+--------------+------------+-------------+------+
| 1 | A | 0 | 1 | 1 | 1 |
| 1 | A | 1 | 2 | 1 | 1 |
| 1 | A | 2 | 3 | 2 | 2 |
| 1 | A | 3 | 4 | 1 | 3 |
| 2 | A | 2 | 3 | 2 | 4 |
| 1 | B | 2 | 3 | 2 | 5 |
| 1 | A | 4 | 5 | 1 | 3 |
+---------+----------+--------------+------------+-------------+------+
我对这个问题的搜索使我找到了这个解决方案:
我尝试进行 grp
计算:
SELECT *, ROW_NUMBER() OVER (ORDER BY GroupId, RouteId, LengthStart) - ROW_NUMBER() OVER (PARTITION BY GroupId, RouteId, LocationId ORDER BY GroupId, RouteId, LengthStart) AS grp
FROM mytable
我尝试采用该解决方案,以便我可以进行更多级别的分区,并且它适用于真正基本的用例(如上面展示的那个,但在复杂的场景中它会失败)。
我真的不完全理解为什么两个 row_number() 相减以及它是如何计算的,但它在这个简单的例子中工作得很好。
我尝试了其他使用 LAG
的方法,但就是无法理解如何理解并应用它。
这里有一个fiddle,场景比较复杂:
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=3704dfe8583b0dd020b189184d149cb7
您可以看到我在这里突出显示的许多错误之一:
这看起来像是一个空岛问题,您误解了解决方案。
在这种情况下,grp
列本身不会识别间隙或孤岛,它只会为您提供 extra 识别它们所需的信息。
在你的例子中,你实现它的方式 (locationId, grp)
形成了新的 "sub_group" 标识符。
我还会稍微更改您的查询,以便于理解正在发生的事情...
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY GroupID, RouteID ORDER BY LengthStart)
- ROW_NUMBER() OVER (PARTITION BY GroupId, RouteId, LocationId ORDER BY LengthStart) AS grp
FROM
mytable
ORDER BY
GroupId, RouteId, LengthStart
这使得不同的 GroupID, RouteID
在计算差距和岛屿 (子组)。
那我可以展示一个简化的例子...
LocationID, Start, SetRowNum, LocRowNum, Difference, GroupID
1 000 1 1 0 (1,0)
1 100 2 2 0 (1,0)
2 200 3 1 2 (2,2)
2 300 4 2 2 (2,2)
1 400 5 3 2 (1,2)
2 500 6 3 3 (2,3)
3 600 7 1 6 (3,6)
2 700 8 4 4 (2,4)
2 800 9 5 4 (2,4)
每个LocationID
分别计算出一个island
.
A gap
只是任何其他 LocationID
的任何行。
"trick" 是在每个岛内,两个行号一起增加。通过两者一起增加,差异保持不变。这个区别是 island_id
.
然后,在间隙期间,第一个行号增加,这样当我们到达下一个岛时,两个行号之间的差异增加,给出一个新的 island_id
**对于那个 LocationID
.
请记住,在计算 location_1
的 island_id
时,我们将 所有其他位置 视为 gaps
在位置 1 的岛屿之间。
对于位置 1,我们在 1,2
和 5
行有岛屿,IslandID
分别为 0
和 2
。
LocationID, Start, SetRowNum, LocRowNum, Difference, GROUP_ID
1 000 1 1 0 (1,0)
1 100 2 2 0 (1,0)
GAP
1 400 5 3 2 (1,2)
对于位置 2,我们在 3,4
、6
和 8,9
行有岛屿,其中 IslandID
共 2
、3
、 4
分别。
LocationID, Start, SetRowNum, LocRowNum, Difference
GAP
2 200 3 1 2 (2,2)
2 300 4 2 2 (2,2)
GAP
2 500 6 3 3 (2,3)
GAP
2 700 8 4 4 (2,4)
2 800 9 5 4 (2,4)
对于位置 3,我们在第 7
行有岛屿,其中 IslandID
个 6
。
LocationID, Start, SetRowNum, LocRowNum, Difference
GAP
3 600 7 1 6 (3,6)
GAP
总而言之,这些岛屿都有不同的ID。
但是 Location1 和 Location2 都有 IslandID = 2 的岛屿。
- 所以,你需要
(locationID, IslandID)
一起使用
- 复合键对于该分区是唯一的
改变over子句的参数会产生不同的结果。请注意,您不需要按顺序重复用于分区的列。该技术的概念是计算一个公共值(在 grp
列中),该值经常用于后续计算,例如;步数,或成本,或 min/max 等
SELECT
*
, ROW_NUMBER() OVER (ORDER BY GroupId, RouteId, LengthStart)
- ROW_NUMBER() OVER (PARTITION BY GroupId, RouteId ORDER BY LengthStart ) AS grp
FROM mytable
ORDER BY
GroupId
, RouteId
, LengthStart
输出两个 row_number 计算结果很有用,这样您就可以看到它是如何工作的:
Id GroupId RouteId LengthStart LengthEnd LocationId rn1 rn2 grp
--------- --------- --------- ------------- ----------- ------------ ----- ----- -----
2651246 3 AAA 0.0000000 0.0920000 1884268 1 1 0
2651247 3 AAA 0.0920000 0.5800000 1855305 2 2 0
2651248 3 AAA 0.5800000 1.3610000 1884268 3 3 0
2651249 3 AAA 1.3610000 1.6170000 1884268 4 4 0
2651250 3 AAA 1.6170000 2.3750000 1884268 5 5 0
2681493 3 BBB 0.0000000 1.5600000 1864963 6 1 5
2681494 3 BBB 1.5600000 2.7100000 1864963 7 2 5
2681495 3 BBB 2.7100000 3.3900000 1864963 8 3 5
2954915 3 CCC 0.0000000 0.0500000 1883382 9 1 8
2954916 3 CCC 0.0500000 0.1400000 1846300 10 2 8
…
rn1
列从 1 开始并不断递增。第二个 rn 列在每个分区中从 1 重新开始,但由于它也在相同的顺序序列上递增 1,因此当您从 rn1
中减去 rn2
时,您会得到 "constant" 结果 grp
用于 rn2
中使用的每个分区
grp
列应表示由 GroupId, RouteId
分区并排序 LengthStart
的块,如果 locationId 保持不变,则当您穿过块时 grp
是一样的。序列中断会创建一个新的 grp
+---------+----------+--------------+------------+-------------+------+
| GROUPID | ROUTEID | LENGTHSTART | LENGTHEND | LOCATIONID | GRP |
+---------+----------+--------------+------------+-------------+------+
| 1 | A | 0 | 1 | 1 | 1 |
| 1 | A | 1 | 2 | 1 | 1 |
| 1 | A | 2 | 3 | 2 | 2 |
| 1 | A | 3 | 4 | 1 | 3 |
| 2 | A | 2 | 3 | 2 | 4 |
| 1 | B | 2 | 3 | 2 | 5 |
| 1 | A | 4 | 5 | 1 | 3 |
+---------+----------+--------------+------------+-------------+------+
我对这个问题的搜索使我找到了这个解决方案:
我尝试进行 grp
计算:
SELECT *, ROW_NUMBER() OVER (ORDER BY GroupId, RouteId, LengthStart) - ROW_NUMBER() OVER (PARTITION BY GroupId, RouteId, LocationId ORDER BY GroupId, RouteId, LengthStart) AS grp
FROM mytable
我尝试采用该解决方案,以便我可以进行更多级别的分区,并且它适用于真正基本的用例(如上面展示的那个,但在复杂的场景中它会失败)。
我真的不完全理解为什么两个 row_number() 相减以及它是如何计算的,但它在这个简单的例子中工作得很好。
我尝试了其他使用 LAG
的方法,但就是无法理解如何理解并应用它。
这里有一个fiddle,场景比较复杂:
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=3704dfe8583b0dd020b189184d149cb7
您可以看到我在这里突出显示的许多错误之一:
这看起来像是一个空岛问题,您误解了解决方案。
在这种情况下,grp
列本身不会识别间隙或孤岛,它只会为您提供 extra 识别它们所需的信息。
在你的例子中,你实现它的方式 (locationId, grp)
形成了新的 "sub_group" 标识符。
我还会稍微更改您的查询,以便于理解正在发生的事情...
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY GroupID, RouteID ORDER BY LengthStart)
- ROW_NUMBER() OVER (PARTITION BY GroupId, RouteId, LocationId ORDER BY LengthStart) AS grp
FROM
mytable
ORDER BY
GroupId, RouteId, LengthStart
这使得不同的 GroupID, RouteID
在计算差距和岛屿 (子组)。
那我可以展示一个简化的例子...
LocationID, Start, SetRowNum, LocRowNum, Difference, GroupID
1 000 1 1 0 (1,0)
1 100 2 2 0 (1,0)
2 200 3 1 2 (2,2)
2 300 4 2 2 (2,2)
1 400 5 3 2 (1,2)
2 500 6 3 3 (2,3)
3 600 7 1 6 (3,6)
2 700 8 4 4 (2,4)
2 800 9 5 4 (2,4)
每个LocationID
分别计算出一个island
.
A gap
只是任何其他 LocationID
的任何行。
"trick" 是在每个岛内,两个行号一起增加。通过两者一起增加,差异保持不变。这个区别是 island_id
.
然后,在间隙期间,第一个行号增加,这样当我们到达下一个岛时,两个行号之间的差异增加,给出一个新的 island_id
**对于那个 LocationID
.
请记住,在计算 location_1
的 island_id
时,我们将 所有其他位置 视为 gaps
在位置 1 的岛屿之间。
对于位置 1,我们在 1,2
和 5
行有岛屿,IslandID
分别为 0
和 2
。
LocationID, Start, SetRowNum, LocRowNum, Difference, GROUP_ID
1 000 1 1 0 (1,0)
1 100 2 2 0 (1,0)
GAP
1 400 5 3 2 (1,2)
对于位置 2,我们在 3,4
、6
和 8,9
行有岛屿,其中 IslandID
共 2
、3
、 4
分别。
LocationID, Start, SetRowNum, LocRowNum, Difference
GAP
2 200 3 1 2 (2,2)
2 300 4 2 2 (2,2)
GAP
2 500 6 3 3 (2,3)
GAP
2 700 8 4 4 (2,4)
2 800 9 5 4 (2,4)
对于位置 3,我们在第 7
行有岛屿,其中 IslandID
个 6
。
LocationID, Start, SetRowNum, LocRowNum, Difference
GAP
3 600 7 1 6 (3,6)
GAP
总而言之,这些岛屿都有不同的ID。
但是 Location1 和 Location2 都有 IslandID = 2 的岛屿。
- 所以,你需要
(locationID, IslandID)
一起使用 - 复合键对于该分区是唯一的
改变over子句的参数会产生不同的结果。请注意,您不需要按顺序重复用于分区的列。该技术的概念是计算一个公共值(在 grp
列中),该值经常用于后续计算,例如;步数,或成本,或 min/max 等
SELECT
*
, ROW_NUMBER() OVER (ORDER BY GroupId, RouteId, LengthStart)
- ROW_NUMBER() OVER (PARTITION BY GroupId, RouteId ORDER BY LengthStart ) AS grp
FROM mytable
ORDER BY
GroupId
, RouteId
, LengthStart
输出两个 row_number 计算结果很有用,这样您就可以看到它是如何工作的:
Id GroupId RouteId LengthStart LengthEnd LocationId rn1 rn2 grp
--------- --------- --------- ------------- ----------- ------------ ----- ----- -----
2651246 3 AAA 0.0000000 0.0920000 1884268 1 1 0
2651247 3 AAA 0.0920000 0.5800000 1855305 2 2 0
2651248 3 AAA 0.5800000 1.3610000 1884268 3 3 0
2651249 3 AAA 1.3610000 1.6170000 1884268 4 4 0
2651250 3 AAA 1.6170000 2.3750000 1884268 5 5 0
2681493 3 BBB 0.0000000 1.5600000 1864963 6 1 5
2681494 3 BBB 1.5600000 2.7100000 1864963 7 2 5
2681495 3 BBB 2.7100000 3.3900000 1864963 8 3 5
2954915 3 CCC 0.0000000 0.0500000 1883382 9 1 8
2954916 3 CCC 0.0500000 0.1400000 1846300 10 2 8
…
rn1
列从 1 开始并不断递增。第二个 rn 列在每个分区中从 1 重新开始,但由于它也在相同的顺序序列上递增 1,因此当您从 rn1
中减去 rn2
时,您会得到 "constant" 结果 grp
用于 rn2