Mysql
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Mysql

Chọn CIDR nằm trong dải IP

Lưu trữ địa chỉ IP trong ký hiệu tứ phân chấm trong VARCHAR không phải là cách tối ưu nhất để lưu trữ chúng, vì dotted-quad là một biểu diễn thân thiện với con người của một số nguyên không dấu 32 bit không tự cho phép lập chỉ mục cơ sở dữ liệu. Nhưng đôi khi về cơ bản nó thuận tiện hơn, và ở quy mô nhỏ, thực tế là các truy vấn yêu cầu quét bảng thường không phải là vấn đề.

MySQL Stored Functions là một cách tốt để đóng gói logic tương đối phức tạp đằng sau một hàm đơn giản có thể được tham chiếu trong một truy vấn, có khả năng dẫn đến các truy vấn dễ hiểu hơn và giảm lỗi sao chép / dán.

Vì vậy, đây là một hàm được lưu trữ mà tôi đã viết có tên là find_ip4_in_cidr4() . Nó hoạt động tương tự như hàm tích hợp sẵn FIND_IN_SET() - bạn cung cấp cho nó một giá trị và bạn cung cấp cho nó một "tập hợp" (thông số CIDR) và nó trả về một giá trị để cho biết liệu giá trị đó có nằm trong tập hợp đó hay không.

Đầu tiên, một minh họa về chức năng đang hoạt động:

Nếu địa chỉ nằm trong khối, hãy trả về độ dài tiền tố. Tại sao trả về độ dài tiền tố? Các số nguyên khác 0 là "true", vì vậy chúng tôi chỉ có thể trả về 1 , nhưng nếu bạn muốn sắp xếp các kết quả phù hợp để tìm ngắn nhất hoặc dài nhất trong số nhiều tiền tố phù hợp, bạn có thể ORDER BY giá trị trả về của hàm.

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)

Không có trong khối? Điều đó trả về 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)

Có một trường hợp đặc biệt cho địa chỉ tất cả các số 0, chúng tôi trả về -1 (vẫn là "true", nhưng giữ nguyên thứ tự sắp xếp):

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)

Các đối số vô nghĩa trả về 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)

Bây giờ, bạn 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 ;

Một vấn đề không dành riêng cho các hàm được lưu trữ, nhưng lại áp dụng cho hầu hết các hàm trên hầu hết các nền tảng RDBMS là khi một cột được sử dụng làm đối số cho một hàm trong WHERE , máy chủ không thể "nhìn ngược lại" thông qua chức năng sử dụng chỉ mục để tối ưu hóa truy vấn.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Tại sao tôi không thể sử dụng secure_rank cho 'điểm xếp hạng' của SQL?

  2. Sự cố khi lưu trữ các giá trị Vĩ độ và Kinh độ trong cơ sở dữ liệu MySQL

  3. 15 Mẹo Tối ưu hóa và Điều chỉnh Hiệu suất MySQL / MariaDB hữu ích

  4. Cách tạo một số trang bằng dompdf

  5. MySQL chọn hàng ngày cuối cùng được sắp xếp theo ngày DESC