如何在MySQL中存储(并找到)带有通配符的字符串?

时间:2012-10-01 11:50:30

标签: php mysql

我需要在其中存储带有通配符的IP地址,例如10.0。%。1。然后,我希望从我的查询中找到任何IP地址在允许范围内(10.0.0-255.1)的人。

你能帮帮我吗?我已经尝试存储10.0。%。1但是找不到LIKE查询(我甚至不知道这是不是这样做 - 可能不是)。

提前致谢。

3 个答案:

答案 0 :(得分:3)

将IP地址转换为32位整数,并为地址和掩码创建一列。您可以使用INET_NTOA()INET_ATON()功能。 10.0。%。1范围可以表示为网络10.0.0.1,掩码为255.255.0.255。要查看给定IP地址是否与此网络匹配,请将掩码应用于IP地址,并将其与网络地址进行比较。如果匹配,则地址在网络中。例如,如果要测试10.0.4.1,则使用按位AND将掩码应用于它:

10.0.4.1 & 255.255.0.255 = 10.0.0.1

这与您的网络地址匹配,因此该IP位于网络中。

您可以将此网络存储在您的表中(ip和mask是整数):

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地址。

答案 1 :(得分:2)

如何将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字段构造一个具有该结构的临时表,但是如果你有很多请求,这将是很多开销。

答案 2 :(得分:0)

我仔细查看了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字符串函数的更多信息(这几乎总是很痛苦)写),见the docs here