W rzeczywistości kolumna liczb całkowitych bez znaku jest już najskuteczniejszym sposobem wyszukiwania dopasowań pod częściowymi adresami IP! Proszę nie marnować energii ani czasu procesora na konwersję z powrotem do notacji z kropkami lub wyszukiwanie LIKE w jakiejś kolumnie z ciągami.
Istnieje kilka sposobów na zapisanie częściowego adresu IP, ale ostatecznie wszystkie sprowadzają się do podstawowego adresu IP z maską sieci. Ponadto, zakładając, że przez częściowe masz na myśli wszystkie adresy IP ze wspólnym prefiksem, jest to również równoważne określeniu zakresu adresów IP.
Tak czy inaczej, częściowa specyfikacja adresu IP zostanie opisana jako dwie 32-bitowe liczby całkowite bez znaku, zakodowane w tym samym formacie, co kolumna bazy danych. Albo masz IP początkowe i IP końcowe, albo masz IP bazowe i maskę. Te liczby całkowite mogą być używane bezpośrednio w zapytaniu SQL, aby efektywnie uzyskiwać dopasowania. Co więcej, jeśli użyjesz podejścia zakresu ip, silnik będzie mógł skorzystać z uporządkowanego indeksu w kolumnie ip. Lepszego nie można oczekiwać.
Jak więc zbudować zasięg IP? Zależy to od tego, w jaki sposób twoje adresy częściowe zostały określone w pierwszej kolejności, ale zakładając, że znasz maskę sieciową, wtedy adres początkowy jest równy (podstawowe ip i maska podsieci), a adres końcowy to ((podstawowe ip &maska sieci) | (~netmask)), gdzie &, | i ~ odpowiednio oznacza bitowe-i, bitowe-lub i bitowe-nie.
Aktualizacja
Oto przykładowy kod do zastosowania opisanej przeze mnie strategii.
Teraz minęło bardzo dużo czasu od ostatniego pisania kodu PHP, a poniższy kod nigdy nie został wykonany, więc proszę wybacz wszelkie błędy, które mogłem wprowadzić. Celowo wybrałem też „rozszerzenie” każdego scenariusza notacji, aby ułatwić jego zrozumienie, zamiast ściskać je wszystkie w jednym, bardzo złożonym wyrażeniu regularnym.
if (preg_match(' /^ (\d{1,3}) [.] (\d{1,3}) [.] (\d{1,3}) [.] (\d{1,3}) [/] (\d{1,2}) $/x', $input, $r)) {
// Four-dotted IP with number of significant bits: 123.45.67.89/24
$a = intval($r[1]);
$b = intval($r[2]);
$c = intval($r[3]);
$d = intval($r[4]);
$mask = intval($r[5]);
} elseif (preg_match(' /^ (\d{1,3}) (?: [.] [*0] [.] [*0] [.] [*0] )? $/x', $input, $r)) {
// Four-dotted IP with three-last numbers missing, or equals to 0 or '*':
// 123.45, 123.45.0.0, 123.45.*.* (assume netmask of 8 bits)
$a = intval($r[1]);
$b = 0;
$c = 0;
$d = 0;
$mask = 8;
} elseif (preg_match(' /^ (\d{1,3}) [.] (\d{1,3}) (?: [.] [*0] [.] [*0] )? $/x', $input, $r)) {
// Four-dotted IP with two-last numbers missing, or equals to 0 or '*':
// 123.45, 123.45.0.0, 123.45.*.* (assume netmask of 16 bits)
$a = intval($r[1]);
$b = intval($r[2]);
$c = 0;
$d = 0;
$mask = 16;
} elseif (preg_match(' /^ (\d{1,3}) [.] (\d{1,3}) [.] (\d{1,3}) (?: [.] [*0] )? $/x', $input, $r)) {
// Four-dotted IP with last number missing, or equals to 0 or *:
// 123.45.67, 123.45.67.0, 123.45.67.* (assume netmask of 24 bits)
$a = intval($r[1]);
$b = intval($r[2]);
$c = intval($r[3]);
$d = 0;
$mask = 24;
} elseif (preg_match(' /^ (\d{1,3}) [.] (\d{1,3}) [.] (\d{1,3}) [.] (\d{1,3}) $/x', $input, $r)) {
// Four-dotted IP: 123.45.67.89 (assume netmask of 32 bits)
$a = intval($r[1]);
$b = intval($r[2]);
$c = intval($r[3]);
$d = intval($r[4]);
$mask = 32;
} else {
throw new Exception('...');
}
if ($a < 0 || $a > 255) { throw new Exception('...') };
if ($b < 0 || $b > 255) { throw new Exception('...') };
if ($c < 0 || $c > 255) { throw new Exception('...') };
if ($d < 0 || $d > 255) { throw new Exception('...') };
if ($mask < 1 || $mask > 32) { throw new Exception('...') };
$baseip = ($a << 24) + ($b << 16) + ($c << 8) + ($d);
$netmask = (1 << (32 - $mask)) - 1;
$startip = $baseip & netmask;
$endip = ($baseip & netmask) | (~netmask);
// ...
doSql( "SELECT ... FROM ... WHERE ipaddress >= ? && ipaddress <= ?", $startip, $endip);
// or
doSql( "SELECT ... FROM ... WHERE ((ipaddress & ?) = ?)", $netmask, $startip);