从您的网络列中,您已经可以看到网络掩码中的位数,并且借助一点点算术运算,可以轻松检测用户ip是否落入该网络。因此,我建议你将该列分成它的(二进制)网络IP和它的cidr号码。
让我解释一下。如果我们按照您提供的第一个示例(10.0.32.0/19),我们可以看到它的网络掩码(“/ 19”位)以二进制表示为19个,其他所有位都设置为零:
11111111 11111111 11100000 00000000
让我们的1.0.32.56样本用户IP:
00000001 00000000 00100000 00111000
你可以看到,如果你采取的按位和/ 19网络掩码与用户IP一起,你会结束:
00000001 00000000 00100000 00000000
...它转换为虚线的点ds为1.0.32.0。看起来熟悉?
无论如何,这是我为你的问题采取的方法。首先,我们需要使用udf将IP地址转换为二进制。我无耻地窃取this answer之一:
CREATE FUNCTION dbo.fnBinaryIPv4(@ip AS VARCHAR(15)) RETURNS BINARY(4)
AS
BEGIN
DECLARE @bin AS BINARY(4)
SELECT @bin = CAST(CAST(PARSENAME(@ip, 4) AS INTEGER) AS BINARY(1))
+ CAST(CAST(PARSENAME(@ip, 3) AS INTEGER) AS BINARY(1))
+ CAST(CAST(PARSENAME(@ip, 2) AS INTEGER) AS BINARY(1))
+ CAST(CAST(PARSENAME(@ip, 1) AS INTEGER) AS BINARY(1))
RETURN @bin
END
GO
我也觉得有帮助的所有的网络掩码在一个小的查找表:
CREATE TABLE netmask (
bits TINYINT PRIMARY KEY,
binary_mask BINARY(4) NOT NULL
)
INSERT INTO netmask (bits, binary_mask) VALUES
(0, 0x00000000), (1, 0x80000000), (2, 0xc0000000), (3, 0xe0000000),
(4, 0xf0000000), (5, 0xf8000000), (6, 0xfc000000), (7, 0xfe000000),
(8, 0xff000000), (9, 0xff800000), (10, 0xffc00000), (11, 0xffe00000),
(12, 0xfff00000), (13, 0xfff80000), (14, 0xfffc0000), (15, 0xfffe0000),
(16, 0xffff0000), (17, 0xffff8000), (18, 0xffffc000), (19, 0xffffe000),
(20, 0xfffff000), (21, 0xfffff800), (22, 0xfffffc00), (23, 0xfffffc00),
(24, 0xffffff00), (25, 0xffffff80), (26, 0xffffffc0), (27, 0xffffffe0),
(28, 0xfffffff0), (29, 0xfffffff8), (30, 0xfffffffc), (31, 0xfffffffe),
(32, 0xffffffff)
接下来我们创建了两个新列和填充其中:
ALTER TABLE GeoIP
ADD binary_network BINARY(4), network_bits TINYINT
GO
UPDATE GeoIP
SET binary_network = dbo.fnBinaryIPv4(SUBSTRING(network, 0, PATINDEX('%/%', network))),
network_bits = CAST(SUBSTRING(network, PATINDEX('%/%', network) + 1, 3) AS TINYINT)
所以现在我们可以重写查询为:
DECLARE @binary_user_ip BIGINT
SELECT @binary_user_ip = dbo.fnBinaryIPv4(@user_ip)
SELECT geoname_id
FROM GeoIP g
JOIN netmask n ON g.network_bits = n.bits
WHERE @binary_user_ip & n.binary_mask = g.binary_network
注 - 这只适用于IPv4。如果你想检测IPv6子网,一般的方法是一样的,但字符串转换和算术会更复杂。
这很好。你能解释这条线是什么吗? (其中@binary_user_ip&n.binary_mask = g.binary_network)。 –
另外,如果我使用binary_network作为我的主要搜索列,我应该索引该列吗? –
该行是按位与。与我之前展示过的二进制例子一样。是的,如果binary_network是你的主要搜索栏,你几乎肯定应该为它编制索引。 – duckbenny