列出连续记录范围的有效方法
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
解决了这个问题,谢谢!
有没有一种好的方法可以用多列做同样的事情?例如,假设我有一个描述列,我想做同样的事情,除了不是对所有连续数字进行分组,我只在描述相同时才这样做以上是关于列出连续记录范围的有效方法的主要内容,如果未能解决你的问题,请参考以下文章