我有一张约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分钟......我怎样才能更快地找到这个值?最好更快?
答案 0 :(得分:2)
我只在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://stackoverflow.com/a/6464763/1078488
答案 1 :(得分:0)
你可以考虑进行一种"二元搜索"。从连续数字[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