Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server SQL在子网中查找IP地址_Sql Server - Fatal编程技术网

Sql server SQL在子网中查找IP地址

Sql server SQL在子网中查找IP地址,sql-server,Sql Server,我们最近开始使用MaxMind Geolite数据库作为基于IP的城市查找。有很多关于将数据导入SQL Server的说明(我已经完成了)。现在我需要弄清楚如何在子网中搜索给定的IP 数据库模式: CREATE TABLE GeoIP ( network varchar(20) not null, geoname_id varchar(20) not null, registered_country_geoname_id varchar(20) not null,

我们最近开始使用MaxMind Geolite数据库作为基于IP的城市查找。有很多关于将数据导入SQL Server的说明(我已经完成了)。现在我需要弄清楚如何在子网中搜索给定的IP

数据库模式:

CREATE TABLE GeoIP (   
    network varchar(20) not null,
    geoname_id varchar(20) not null,
    registered_country_geoname_id varchar(20) not null,
    represented_country_geoname_id varchar(20) not null,
    is_anonymous_proxy int,
    is_satellite_provider int,
    postal_code varchar(20),
    latitude Decimal(9,6),
    longitude Decimal(9,6),
    accuracy_radius int
); 
“网络”列具有IP/子网的行数据(例如:1.0.32.0/19、1.0.64.0/20、1.0.80.0/22)

给定一个IP地址,我试图编写一个SELECT语句来返回geoname\u id

Ex: SELECT geoname_id FROM GeoIP where @user_ip in {some expression}
我希望这样做,而不必将网络列分解为“low_ip”和“high_ip”BIGINT列。但是,如果这是唯一的方法,我还可以在如何编写全局更新语句以从现有数据中添加这些列方面使用一些帮助

SQL SERVER 2008需要此功能,因此无法使用任何很酷的Postgres等功能


谢谢

从您的网络专栏中,您已经可以看到网络掩码中的位数,并且借助一点位运算,可以轻松检测用户ip是否属于该网络。因此,我建议您将该列拆分为其(二进制)网络ip和cidr号

让我解释一下。如果我们以您提供的第一个示例(10.0.32.0/19)为例,我们可以看到它的网络掩码(“19”位)以二进制形式表示为19位,所有其他位都设置为零:

11111111 11111111 11100000 00000000
让我们以1.0.32.56的用户ip为例:

00000001 00000000 00100000 00111000
您可以看到,如果将该/19网络掩码的位AND与用户ip一起使用,您将得到:

00000001 00000000 00100000 00000000
…将虚线四边形转换为1.0.32.0。看起来眼熟吗

不管怎样,这是我解决你问题的方法。首先,我们需要使用udf将ip地址转换为二进制。我无耻地从以下地方偷了一个:

我还发现,将所有网络掩码放在一个小的查找表中也很有帮助:

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, 0xfffffe00),
    (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\u user\u ip&n.binary\u mask=g.binary\u network)。此外,如果我使用binary\u network作为我的主要搜索列,我应该为该列编制索引吗?该行是按位和。和我之前展示的二进制示例一样。是的,如果binary_network是您的主要搜索列,您几乎肯定应该为其编制索引。@duckbenny您的答案很好,但是,[netmask]table insert子句中有一个错误。位[23]的二进制屏蔽值应为[0xfffffe00]。向mods道歉,我刚刚加入,我的声誉太低,无法发表评论。
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