有效地找到表中不存在的第一个数字?
Posted
技术标签:
【中文标题】有效地找到表中不存在的第一个数字?【英文标题】:efficiently find first number that do not exist in a table? 【发布时间】:2016-01-03 22:25:04 【问题描述】:我有一个大约 528829 行的表,看起来像
CREATE TABLE `ips` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`ip` INTEGER NOT NULL DEFAULT NULL,
`scantime` INTEGER NULL DEFAULT NULL,
`pingable` INTEGER NULL DEFAULT NULL
);
现在我需要找到ip
中不存在的第一个数字,从 0 开始到 4294967295(又名 0xFFFFFFFF),
目前我只是使用
function isScanned($ip)
static $isScannedStm=false;
static $boundip=0;
if($isScannedStm===false)
global $db;
$isScannedStm=$db->prepare('SELECT 1 FROM `ips` WHERE `ip` = :ip LIMIT 1');
$isScannedStm->bindParam(':ip',$boundip,PDO::PARAM_INT);
return isScanned($ip);
$boundip=$ip;
$isScannedStm->execute();
//var_dump($isScannedStm->fetch(PDO::FETCH_NUM));
return !!($isScannedStm->fetch(PDO::FETCH_NUM));
//~~~
while(isScanned($i))
++$i;
..它可以工作,但有 528829 行,在我的 Intel Atom C2750 @ 2.4GHz 上需要超过 1 小时 30 分钟。我怎样才能更快地找到这个值?最好快得多?
【问题讨论】:
如果这是您的实际代码,您的创建表AUTOINCREMENT
应该读作 AUTO_INCREMENT
dev.mysql.com/doc/refman/5.7/en/example-auto-increment.html。
@Fred-ii-nope,我使用 sqlite,在 SQLite 中,它的 AUTOINCREMENT ,他们无法就名称达成一致^^
啊,我不知道。谢谢(你的)信息。 (注)。
Find mininum not used value in mysql table的可能重复
@JimL 如果您要发布答案,请不要发布重复内容。如果问题因此而关闭,那么没有其他人可以添加额外/替代答案以可能改进问题。这是一种垄断形式。如果我没记错的话,这不是你第一次这样做,也是在你发布答案一分钟后。
【参考方案1】:
我只在 MySQL 中测试过,希望它也适用于 SQLite
SELECT ips.ip+1 AS Missing
FROM ips
LEFT JOIN ips AS next ON ips.ip+1 = next.ip
WHERE next.ip IS NULL
ORDER BY ips.ip LIMIT 1;
Caspar 和 splattru 的解决方案:https://***.com/a/6464763/1078488
【讨论】:
@hanshenrik 太棒了!与旧的做事方式相比,性能如何? 1 小时 30 分钟变成了 @hanshenrik 进步很大,如果你遇到卡斯帕,我想你应该喝一杯啤酒 ^^ 很好,遇到(并帮助!)同胞总是很有趣 有趣的统计数据,它始终以 4.0X 秒的速度运行。在 ip 上添加一个 UNIQUE INDEX 会将其降低到一致的 1.3X 秒 ^^ @hanshenrik 结果非常惊人。 Lmk 如果你做一些与 web/php/js 相关的事情,我的聊天列表上总是需要更多的“本地人”^^【参考方案2】:您可以考虑执行一种“二分搜索”。从连续数字的前半部分开始[1, 2, 3, ... (n/2)]
如果结果的数量不等于当前列表中的连续值的数量,那么您可以拆分初始列表并通过相同的逻辑重新运行,直到您到达第一个不连续的 id。
否则,如果计数匹配,您将转到另一半连续 id。
您的查询将需要包含WHERE...IN
子句。
这不会完全适合你,但也许这会有所帮助:
// Populate current set of consecutive integers
$list = array_fill(0, $count/2);
$listQuery = implode(',', $list);
global $db;
$isScannedStm = $db->prepare('
SELECT 1 FROM `ips`
WHERE `ip` IN ('.$listQuery.')
GROUP BY `ip`
ORDER BY `ip` ASC
');
$isScannedStm->execute()
// Check num results
if (count($list) !== $isScannedStm->fetch(PDO::FETCH_NUM))
// Split the initial list in half
// OR loop through results and find when the ids are not consecutive
可能有更简单的方法可以做到这一点,也许可以考虑查看this question
【讨论】:
以上是关于有效地找到表中不存在的第一个数字?的主要内容,如果未能解决你的问题,请参考以下文章
如何用sql语句查询:在一个表中存在而另一个表中不存在的第一条记录?