列出连续记录范围的有效方法
Efficient way to list ranges of consecutive records
我有一个 table 设置如下:
CREATE TABLE `cn` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`type` int(3) unsigned NOT NULL,
`number` int(10) NOT NULL,
`desc` varchar(64) NOT NULL,
`datetime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
number
通常但 不一定 是唯一的。
大部分 table 由具有连续 number
个条目的行组成。
例如
101010、101011、101012 等
我一直在努力寻找一种 高效 的方法来列出连续数字的范围,以便我可以轻松地找出数字 "missing" 的位置。我想做的是列出开始编号、结束编号和连续行数。由于可能存在重复,我使用 SELECT DISTINCT(number)
来避免重复。
我运气不太好 - 大多数这类问题都与日期有关,很难一概而论。一个查询永远在执行,所以这是不行的。 有点接近但不完全是。它使用 CROSS JOIN
,当您拥有数百万条记录时,这听起来像是一场灾难。
最好的方法是什么?一些答案使用连接,我对性能明智持怀疑态度。现在只有 50,000 行,但几天之内就会有数百万条记录,因此每一盎司的性能都很重要。
我想到的最终伪查询是这样的:
SELECT DISTINCT(number) FROM cn WHERE type = 1 GROUP BY [consecutive...] ORDER BY number ASC
这是一个缺口和孤岛问题。你可以通过row_number()
和number
之间的差异来定义组来解决;差距由差异的变化确定:
select type, min(number) first_number, max(number) last_number, count(*) no_records
from (
select cn.*, row_number() over(order by number) rn
from cn
where type = 1
) c
group by type, number - rn
注意:window 函数在 MySQL 8.0 和 MariaDB 10.3 及更高版本中可用。
在早期版本中,您可以使用会话变量模拟 row_number()
:
select type, min(number) first_number, max(number) last_number, count(*) no_records
from (
select c.*, @rn := @rn + 1 rn
from (select * from cn where type = 1 order by number) c
cross join (select @rn := 0) r
) c
group by number - rn
我有一个 table 设置如下:
CREATE TABLE `cn` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`type` int(3) unsigned NOT NULL,
`number` int(10) NOT NULL,
`desc` varchar(64) NOT NULL,
`datetime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
number
通常但 不一定 是唯一的。
大部分 table 由具有连续 number
个条目的行组成。
例如
101010、101011、101012 等
我一直在努力寻找一种 高效 的方法来列出连续数字的范围,以便我可以轻松地找出数字 "missing" 的位置。我想做的是列出开始编号、结束编号和连续行数。由于可能存在重复,我使用 SELECT DISTINCT(number)
来避免重复。
我运气不太好 - 大多数这类问题都与日期有关,很难一概而论。一个查询永远在执行,所以这是不行的。 CROSS JOIN
,当您拥有数百万条记录时,这听起来像是一场灾难。
最好的方法是什么?一些答案使用连接,我对性能明智持怀疑态度。现在只有 50,000 行,但几天之内就会有数百万条记录,因此每一盎司的性能都很重要。
我想到的最终伪查询是这样的:
SELECT DISTINCT(number) FROM cn WHERE type = 1 GROUP BY [consecutive...] ORDER BY number ASC
这是一个缺口和孤岛问题。你可以通过row_number()
和number
之间的差异来定义组来解决;差距由差异的变化确定:
select type, min(number) first_number, max(number) last_number, count(*) no_records
from (
select cn.*, row_number() over(order by number) rn
from cn
where type = 1
) c
group by type, number - rn
注意:window 函数在 MySQL 8.0 和 MariaDB 10.3 及更高版本中可用。
在早期版本中,您可以使用会话变量模拟 row_number()
:
select type, min(number) first_number, max(number) last_number, count(*) no_records
from (
select c.*, @rn := @rn + 1 rn
from (select * from cn where type = 1 order by number) c
cross join (select @rn := 0) r
) c
group by number - rn