列出连续记录范围的有效方法

Posted

技术标签:

【中文标题】列出连续记录范围的有效方法【英文标题】:Efficient way to list ranges of consecutive records 【发布时间】:2020-06-08 13:25:40 【问题描述】:

我有一个这样设置的表:

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 通常但不一定必须是唯一的。

该表的大部分内容由具有连续 number 条目的行组成。

例如

101010、101011、101012等

我一直在尝试找到一种有效的方法来列出连续数字的范围,以便我可以轻松找出数字“丢失”的位置。我想做的是列出开始编号、结束编号和连续行数。由于可能有重复,我使用SELECT DISTINCT(number) 来避免重复。

我运气不太好 - 大多数这类问题都与日期有关,而且很难一概而论。一个查询永远执行,所以这是不行的。 This answer 有点接近但不完全。它使用CROSS JOIN,当您拥有数百万条记录时,这听起来像是灾难的秘诀。

最好的方法是什么?一些答案使用连接,我对性能表示怀疑。目前只有 50,000 行,但几天内将有数百万条记录,因此每一盎司的性能都很重要。

我想到的最终伪查询类似于:

SELECT DISTINCT(number) FROM cn WHERE type = 1 GROUP BY [consecutive...] ORDER BY number ASC

【问题讨论】:

【参考方案1】:

这是一个孤岛问题。您可以通过使用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

注意:窗口函数在 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

【讨论】:

即使行顺序是任意的,这也能工作吗?连续的项目不一定按顺序添加到表中。另外,我只需要type = 1 的结果,所以我可以不用分区。现在正在尝试测试这个 好的,现在查询执行了,但它似乎不能正常工作。例如,我得到一个数字作为第一个和最后一个,然后下一行的数字紧随其后,依此类推。它基本上似乎只是在枚举所有内容。 偶尔 num_records 大于1,但通常不起作用。这是否依赖于以number 顺序插入的所有行? (对我来说不是这样) 看起来像在最里面的查询中添加GROUP BY number 解决了这个问题,谢谢! 有没有一种好的方法可以用多列做同样的事情?例如,假设我有一个描述列,我想做同样的事情,除了不是对所有连续数字进行分组,我只在描述相同时才这样做

以上是关于列出连续记录范围的有效方法的主要内容,如果未能解决你的问题,请参考以下文章

从数组中识别和收集连续范围[重复]

在 C++ 中有效地保存许多连续记录的图像

Oracle中获取连续的序列号范围的SQL

Oracle中获取连续的序列号范围的SQL

在C ++中有效地保存了许多连续记录的图像

HIVE SQL将连续范围折叠成单行