sql >> Database >  >> RDS >> Mysql

Selecteer CIDR dat binnen het IP-bereik valt

IP-adressen opslaan in gestippelde quad-notatie in een VARCHAR is niet de meest optimale manier om ze op te slaan, aangezien dotted-quad een mensvriendelijke weergave is van een 32-bits geheel getal zonder teken dat zich niet leent voor database-indexering. Maar soms is het fundamenteel handiger, en op kleine schaal is het feit dat query's een tabelscan vereisen meestal geen probleem.

Opgeslagen MySQL-functies zijn een goede manier om relatief complexe logica in te kapselen achter een eenvoudige functie waarnaar in een query kan worden verwezen, wat mogelijk kan leiden tot gemakkelijker te begrijpen query's en kopieer- en plakfouten vermindert.

Dus, hier is een opgeslagen functie die ik heb geschreven met de naam find_ip4_in_cidr4() . Het werkt enigszins vergelijkbaar met de ingebouwde functie FIND_IN_SET() -- je geeft het een waarde en je geeft het een "set" (CIDR-specificatie) en het retourneert een waarde om aan te geven of de waarde in de set zit.

Eerst een illustratie van de functie in actie:

Als het adres zich binnen het blok bevindt, retourneer dan de lengte van het voorvoegsel. Waarom de lengte van het voorvoegsel retourneren? Niet-nul gehele getallen zijn "true", dus we kunnen gewoon 1 . teruggeven , maar als u de overeenkomende resultaten wilt sorteren om de kortste of langste van meerdere overeenkomende voorvoegsels te vinden, kunt u ORDER BY de geretourneerde waarde van de functie.

mysql> SELECT find_ip4_in_cidr4('203.0.113.123','203.0.113.0/24');
+-----------------------------------------------------+
| find_ip4_in_cidr4('203.0.113.123','203.0.113.0/24') |
+-----------------------------------------------------+
|                                                  24 |
+-----------------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT find_ip4_in_cidr4('192.168.100.1','192.168.0.0/16');
+-----------------------------------------------------+
| find_ip4_in_cidr4('192.168.100.1','192.168.0.0/16') |
+-----------------------------------------------------+
|                                                  16 |
+-----------------------------------------------------+
1 row in set (0.00 sec)

Niet in het blok? Dat levert 0 (false) op.

mysql> SELECT find_ip4_in_cidr4('192.168.100.1','203.0.113.0/24');
+-----------------------------------------------------+
| find_ip4_in_cidr4('192.168.100.1','203.0.113.0/24') |
+-----------------------------------------------------+
|                                                   0 |
+-----------------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT find_ip4_in_cidr4('192.168.100.1','192.168.0.0/24');
+-----------------------------------------------------+
| find_ip4_in_cidr4('192.168.100.1','192.168.0.0/24') |
+-----------------------------------------------------+
|                                                   0 |
+-----------------------------------------------------+
1 row in set (0.00 sec)

Er is een speciaal geval voor het adres met alle nullen, we retourneren -1 (nog steeds "waar", maar behoudt de sorteervolgorde):

mysql> SELECT find_ip4_in_cidr4('192.168.100.1','0.0.0.0/0');
+------------------------------------------------+
| find_ip4_in_cidr4('192.168.100.1','0.0.0.0/0') |
+------------------------------------------------+
|                                             -1 |
+------------------------------------------------+
1 row in set (0.00 sec)

Onzin argumenten retourneren null:

mysql> SELECT find_ip4_in_cidr4('234.467.891.0','192.168.0.0/24');
+-----------------------------------------------------+
| find_ip4_in_cidr4('234.467.891.0','192.168.0.0/24') |
+-----------------------------------------------------+
|                                                NULL |
+-----------------------------------------------------+
1 row in set (0.00 sec)

Nu, de codez:

DELIMITER $$

DROP FUNCTION IF EXISTS `find_ip4_in_cidr4` $$
CREATE DEFINER=`mezzell`@`%` FUNCTION `find_ip4_in_cidr4`(
  _address VARCHAR(15), 
  _block VARCHAR(18)
) RETURNS TINYINT
DETERMINISTIC /* for a given input, this function always returns the same output */
CONTAINS SQL /* the function does not read from or write to tables */
BEGIN

-- given an IPv4 address and a cidr spec,
-- return -1 for a valid address inside 0.0.0.0/0
-- return prefix length if the address is within the block,
-- return 0 if the address is outside the block,
-- otherwise return null

DECLARE _ip_aton INT UNSIGNED DEFAULT INET_ATON(_address);
DECLARE _cidr_aton INT UNSIGNED DEFAULT INET_ATON(SUBSTRING_INDEX(_block,'/',1));
DECLARE _prefix TINYINT UNSIGNED DEFAULT SUBSTRING_INDEX(_block,'/',-1);
DECLARE _bitmask INT UNSIGNED DEFAULT (0xFFFFFFFF << (32 - _prefix)) & 0xFFFFFFFF;

RETURN CASE /* the first match, not "best" match is used in a CASE expression */
  WHEN _ip_aton IS NULL OR _cidr_aton IS NULL OR /* sanity checks */
       _prefix  IS NULL OR _bitmask IS NULL OR
       _prefix NOT BETWEEN 0 AND 32 OR
       (_prefix = 0 AND _cidr_aton != 0) THEN NULL
  WHEN _cidr_aton = 0 AND _bitmask = 0 THEN -1
  WHEN _ip_aton & _bitmask = _cidr_aton & _bitmask THEN _prefix /* here's the only actual test needed */
  ELSE 0 END;

END $$
DELIMITER ;

Een probleem dat niet specifiek is voor opgeslagen functies, maar eerder van toepassing is op de meeste functies op de meeste RDBMS-platforms, is dat wanneer een kolom wordt gebruikt als argument voor een functie in WHERE , kan de server niet "achteruit kijken" via de functie om een ​​index te gebruiken om de zoekopdracht te optimaliseren.



  1. Django- Hoe berichten die tussen gebruikers worden verzonden in kaart te brengen?

  2. Hoe Teamcity te implementeren met PostgreSQL voor hoge beschikbaarheid

  3. complexe SQL-query, veel tot veel

  4. Hoe kan ik datetime naar datum converteren, de tijden inkorten en de datums achterlaten?