從您的網絡列中,您已經可以看到網絡掩碼中的位數,並且藉助一點點算術運算,可以輕鬆檢測用戶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