Mysql
 sql >> база данни >  >> RDS >> Mysql

Изберете CIDR, който е в обхвата на IP

Съхраняване на IP адреси в точкова четворна нотация в VARCHAR не е най-оптималния начин за съхраняването им, тъй като dotted-quad е удобно за човека представяне на 32-битово цяло число без знак, което не се поддава на индексиране в база данни. Но понякога е фундаментално по-удобно и в малък мащаб фактът, че заявките изискват сканиране на таблица, обикновено не е проблем.

Съхранените функции на MySQL са добър начин за капсулиране на относително сложна логика зад проста функция, която може да бъде препратена в заявка, което потенциално води до по-лесни за разбиране заявки и намалява грешките при копиране/поставяне.

И така, ето съхранена функция, която написах, наречена find_ip4_in_cidr4() . Работи донякъде подобно на вградената функция FIND_IN_SET() -- давате му стойност и му давате "набор" (CIDR спецификация) и той връща стойност, за да посочи дали стойността е в набора.

Първо, илюстрация на функцията в действие:

Ако адресът е вътре в блока, върнете дължината на префикса. Защо да връщате дължината на префикса? Цели числа, различни от нула, са „истина“, така че можем просто да върнем 1 , но ако искате да сортирате съответстващите резултати, за да намерите най-краткия или най-дългия от множество съвпадащи префикси, можете да ORDER BY върнатата стойност на функцията.

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)

Не сте в блока? Това връща 0 (false).

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)

Има специален случай за адреса с всички нули, връщаме -1 (все още е „вярно“, но запазва реда на сортиране):

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)

Безсмислените аргументи връщат нула:

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)

Сега кодът:

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 ;

Проблем, който не е специфичен за съхранените функции, а по-скоро се отнася за повечето функции на повечето RDBMS платформи, е, че когато колона се използва като аргумент на функция в WHERE , сървърът не може да „погледне назад“ чрез функцията, за да използва индекс за оптимизиране на заявката.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Създайте само ЕДНА таблица с Flask + SqlAlchemy

  2. Как да получа първичния ключ за автоматично нарастване на MySQL от Sequelize при save()?

  3. Използване на подготвени отчети с курсор

  4. Тип/структура на данни за съхраняване на изместване на часовата зона в MySQL

  5. MySQL Съвпадение срещу с множество стойности до ПРОТИВ