Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Suche nach Teil-IP-Adressen, die als Ganzzahlen gespeichert sind

Tatsächlich ist die Spalte mit vorzeichenlosen Ganzzahlen bereits die effizienteste Methode, um nach Übereinstimmungen auf Teil-IP-Adressen zu suchen! Bitte verschwenden Sie weder Ihre Energie noch Ihre CPU-Zeit mit der Konvertierung zurück in die Punktnotation oder mit einer LIKE-Suche in irgendeiner Art von String-Spalte.

Es gibt mehrere Möglichkeiten, partielle IP-Adressen aufzuschreiben, aber am Ende laufen sie alle auf eine Basis-IP mit einer Netzmaske hinaus. Unter der Annahme, dass Sie mit teilweise alle IPs mit einem gemeinsamen Präfix meinen, dann ist dies auch gleichbedeutend mit der Angabe eines Bereichs von IPs.

In jedem Fall wird die partielle IP-Adressspezifikation als zwei 32-Bit-Ganzzahlen ohne Vorzeichen beschrieben, die im selben Format wie Ihre Datenbankspalte codiert sind. Entweder Sie haben eine Start-IP und eine End-IP oder Sie haben eine Basis-IP und eine Maske. Diese Ganzzahlen können direkt in Ihrer SQL-Abfrage verwendet werden, um Übereinstimmungen effizient zu erhalten. Noch besser, wenn Sie den IP-Range-Ansatz verwenden, kann die Engine einen geordneten Index für Ihre IP-Spalte nutzen. Besser kann man es nicht erwarten.

Wie baut man also den IP-Bereich auf? Wir werden das davon abhängen, wie Ihre partiellen Adressen ursprünglich angegeben wurden, aber vorausgesetzt, Sie kennen die Netzmaske, dann ist die Startadresse gleich (Basis-IP &Netzmaske) und die Endadresse ist ((Basis-IP &Netzmaske) | (~Netzmaske)), wobei &, | und ~ bedeutet jeweils bitweise-und, bitweise-oder und bitweise-nicht.

Aktualisieren

Hier ist ein Beispielcode, um die von mir beschriebene Strategie anzuwenden.

Nun, es ist sehr lange her, seit ich das letzte Mal PHP-Code geschrieben habe, und das Folgende wurde noch nie ausgeführt, also entschuldigen Sie bitte alle Fehler, die ich möglicherweise eingeführt habe. Ich habe mich auch bewusst dafür entschieden, jedes Notationsszenario zu "erweitern", um es leichter verständlich zu machen, anstatt sie alle in eine einzige, sehr komplexe Regex zu quetschen.

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);