我需要存储一个带有通配符的 IP 地址,例如 10.0.%.1。然后,我希望从我的查询中找到 IP 地址在允许范围 (10.0.0-255.1) 内的任何人。
你们能帮帮我吗?我已经尝试存储 10.0.%.1 但无法使用 LIKE 查询找到它(我什至不知道这是否是这样做的方法 - 可能不是)。
提前致谢。
将 IP 地址转换为 32 位整数,并为地址和掩码创建一列。您可以使用INET_NTOA()
和INET_ATON()
功能。您的 10.0.%.1 范围可以表示为网络 10.0.0.1,掩码为 255.255.0.255。要查看给定 IP 地址是否与此网络匹配,请将掩码应用于 IP 地址,并将其与网络地址进行比较。如果它们匹配,则地址在网络中。例如,如果您想测试 10.0.4.1,您可以使用按位与将掩码应用于它:
10.0.4.1 & 255.255.0.255 = 10.0.0.1
这与您的网络地址匹配,因此该 IP 在网络中。
您可以像这样将此网络存储在您的表中(ip 和掩码是整数):
CREATE TABLE networks (id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, ip INT, mask INT);
INSERT INTO networks (ip, mask)
VALUES (INET_ATON('10.0.0.1'), INET_ATON('255.255.0.255'));
您可以查看给定 IP (10.0.23.1) 是否匹配,如下所示:
SELECT * FROM networks
WHERE ip & mask = INET_ATON('10.0.23.1') & mask
但是,如果您希望它们可读,这会将 IP 选择为整数:
SELECT id, INET_NTOA(ip) AS ipstring, INET_NTOA(mask) AS maskstring
FROM networks
WHERE ip & mask = INET_ATON('10.0.23.1') & mask
这允许您测试单个给定 IP 地址是否与网络匹配。如果要测试一系列 IP,则必须将掩码应用于该范围内的每个 IP,并按上述方式测试每个 IP。您可以在单个查询中完成,只需列出您正在测试的每个 IP 地址。
您如何将 IP 地址保存为数据库中的 4 个整数,如下所示:
IPpart1 IPpart2 IPpart3 IPpart4
-------------------------------
127 0 0 1
并使它们可以为空。(NULL 将成为我们的通配符)
然后,您可以像这样对数据库进行查询:
SELECT * FROM iptable WHERE (IPpart1 IS NULL OR IPpart1 = '$firstpart') AND (IPpart2 IS NULL OR IPpart2 = '$secondpart') AND (IPpart3 IS NULL OR IPpart3 = '$thirdpart') AND (IPpart4 IS NULL OR IPpart4 = '$fourthpart')
这有点冗长,它可以完成工作。
甚至应该可以从普通 IP 字段构造具有该结构的临时表,但是如果您有很多请求,那将是很多开销。
我仔细查看了 MySQL 文档页面,并在那里找到了符合您需求的帖子:
SELECT '10.0.0.1' AS IP0,
( SUBSTRING_INDEX( '10.0.0.1', '.', 1 ) * 16777216 +
SUBSTRING_INDEX(SUBSTRING_INDEX( '10.0.0.1', '.', 2 ),'.',-1) * 65536 +
SUBSTRING_INDEX(SUBSTRING_INDEX( '10.0.0.1', '.', -2 ),'.',1) * 256 +
SUBSTRING_INDEX( '10.0.0.1', '.', -1 )
),
'10.0.255.1' AS IP1,
( SUBSTRING_INDEX( '10.0.255.1', '.', 1 ) * 16777216 +
SUBSTRING_INDEX(SUBSTRING_INDEX( '10.0.255.1', '.', 2 ),'.',-1) * 65536 +
SUBSTRING_INDEX(SUBSTRING_INDEX( '10.0.255.1', '.', -2 ),'.',1) * 256 +
SUBSTRING_INDEX( '10.0.255.1', '.', -1 )
) AS IP2Num1
返回每个 IP 的数值:
Ip: | 10.0.0.1 | 10.0.255.1 |
--------------------------------
Num: | 167772161 | 167837441 |
换句话说:
SELECT foo
FROM bar.foobar
WHERE ( SUBSTRING_INDEX( ipField, '.', 1 ) * 16777216 +
SUBSTRING_INDEX(SUBSTRING_INDEX( ipField, '.', 2 ),'.',-1) * 65536 +
SUBSTRING_INDEX(SUBSTRING_INDEX( ipField, '.', -2 ),'.',1) * 256 +
SUBSTRING_INDEX( ipField, '.', -1 )
) BETWEEN 167772161 AND 167837441;
好吧,你可以这样做:
WHERE ipField LIKE '10.0.%.1'
AND (CAST
(REPLACE(SUBSTRING_INDEX(ipField,'.',3),
CONCAT(SUBSTRING_INDEX(ipField ,'.',2),'.')
,'')
AS UNSIGNED) BETWEEN 0 AND 255)
我使用以下查询进行了测试:
SELECT IF ('10.0.12.1' LIKE '10.0.%.1'
AND (CAST
(REPLACE( SUBSTRING_INDEX('10.0.12.1','.',3),
CONCAT(SUBSTRING_INDEX('10.0.12.1','.',2),'.')
,'')
AS UNSIGNED) BETWEEN 0 AND 255),
TRUE,
FALSE);
它返回了1
,但是,老实说,这有点小题大做,不是吗?为什么不简单:
WHERE ipField REGEXP '10\\.0\\.[0-9]{1,3}\\.1'
你可以稍微调整一下这个表达式,但基本上,在我看来,这似乎是查询这些 IP 地址的最简单方法。
有关 MySQL 字符串函数的更多信息(编写起来几乎总是很痛苦),请参阅此处的文档