如何在SQL Server中比较IP地址?

时间:2015-11-15 08:34:26

标签: c# sql sql-server

我需要执行比较两个ip地址。

这是模型:

public partial class CityIp
{
    [Key]
    public string IpStart { get; set; }
    public string IpEnd { get; set; }
    public string Country { get; set; }
    public string Province { get; set; }
    public string City { get; set; }
}

我的数据库拥有上面模型的6M行。 我得到一个IP地址,我必须明白:它是在ipStart和ipEnd之间吗? 我将ipStart和ipEnd存储为nvarchar,但我不确定它是否正确。

比较它的最佳方法是什么?

我认为从db中提取所有行并在C#代码中进行比较并不好。我更喜欢使用SQL,但我不知道该怎么做。

我需要执行类似

的操作
SELECT 
    Country, Province, City 
FROM 
    CityIP 
WHERE 
    ipStart <= 'myIp' AND 'myIp' <= ipEnd

2 个答案:

答案 0 :(得分:1)

这就是诀窍:

close()

不幸的是,查询速度很慢,因为需要进行全面扫描。

EDIT 以上查询将与ipv4地址一起使用。为了处理ipv6地址,我将使用如下的CLR标量函数:

DECLARE @ipaddress nvarchar(16) = N'87.217.221.22'

SELECT *
FROM CityIp
WHERE
    (
        CAST(PARSENAME(IpStart, 4) AS bigint) * 256 * 256 * 256 +
        CAST(PARSENAME(IpStart, 3) AS bigint) * 256 * 256 +
        CAST(PARSENAME(IpStart, 2) AS bigint) * 256 +
        CAST(PARSENAME(IpStart, 1) AS bigint)
    ) 
    <=
    (
        CAST(PARSENAME(@ipaddress, 4) AS bigint) * 256 * 256 * 256  +
        CAST(PARSENAME(@ipaddress, 3) AS bigint) * 256 * 256 +
        CAST(PARSENAME(@ipaddress, 2) AS bigint) * 256 + 
        CAST(PARSENAME(@ipaddress, 1) AS bigint)
    ) 
    AND
    (
        CAST(PARSENAME(IpEnd, 4) AS bigint) * 256 * 256 * 256 +
        CAST(PARSENAME(IpEnd, 3) AS bigint) * 256 * 256 +
        CAST(PARSENAME(IpEnd, 2) AS bigint) * 256 +
        CAST(PARSENAME(IpEnd, 1) AS bigint)
    ) 
    >=
    (
        CAST(PARSENAME(@ipaddress, 4) AS bigint) * 256 * 256 * 256  +
        CAST(PARSENAME(@ipaddress, 3) AS bigint) * 256 * 256 +
        CAST(PARSENAME(@ipaddress, 2) AS bigint) * 256 + 
        CAST(PARSENAME(@ipaddress, 1) AS bigint)
    )   

然后像这样使用它:

public partial class UserDefinedFunctions
{
    [SqlFunction(DataAccess=DataAccessKind.None, IsDeterministic=true, IsPrecise=true, SystemDataAccess=SystemDataAccessKind.None)]
    [return: SqlFacet(MaxSize=16)]
    public static  SqlBinary TryParseIPAddress([SqlFacet(MaxSize=40)] SqlString iPAddress)
    {
        if (iPAddress.IsNull) return SqlBinary.Null;
        IPAddress address;
        if (IPAddress.TryParse(iPAddress.Value, out address))
        {
            return new SqlBinary(address.GetAddressBytes());
        }
        else return SqlBinary.Null;
    }
}

编辑: 加快速度的一件事是添加两个二进制计算列,以二进制格式存储IpStart和IpEnd。但最大的性能改进是应用Relational Interval Tree,如下文所示:

Interval Queries in SQL Server

答案 1 :(得分:1)

1)我们应该使用VARBINARY(16)存储ip地址!

[Ip] VARBINARY (16) NOT NULL

2)要比较ip地址是否在范围内,你可以写

SELECT * FROM CityIp WHERE IpStart <= @IpAddress AND @IpAddress <= IpEnd

IpStart,IpAddress,IpEnd是VARBINARY(16)。

3)要使用ip地址,我们应该使用System.Net.IPAddress。您可以调用GetAddressBytes()来获取ip的字节

所以,我的模型看起来像这样

public partial class CityIp
{
    [Key]
    public byte[] IpStart { get; set; }
    public byte[] IpEnd { get; set; }
    public string Country { get; set; }
    public string Province { get; set; }
    public string City { get; set; }
}