2015-11-15 44 views
1

我需要執行比較兩個IP地址。如何比較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; } 
} 

我的DB持有上述模型的6M行。 我得到一個IP地址,我必須明白:它是介於ipStart和ipEnd之間? 我將ipStart和ipEnd存儲爲nvarchar,但我不確定它是否正確。

什麼是最好的方法來比較它?

我認爲從數據庫中拉出所有行並將其與C#代碼進行比較並不好。我更喜歡用SQL來做,但我不知道該怎麼做。

我需要一個像

SELECT 
    Country, Province, City 
FROM 
    CityIP 
WHERE 
    ipStart <= 'myIp' AND 'myIp' <= ipEnd 
+5

你是如何存儲的IP地址?您應該將它們存儲爲「int」(用於IPv4)或「varbinary(16)」用於IPv4 + IPv6,而不是字符串。 – Dai

+0

另外,您應確保這些IP地址範圍內部連續。 – Dai

+0

如果您將它們存儲爲字符串(!),這可能會幫助您https://msdn.microsoft.com/en-us/library/ms187928.aspx –

回答

1

1)我們應該用VARBINARY(16)存儲IP地址!

​​

2)比較,如果一個IP地址範圍內,您可以只寫

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

凡IpStart,ip地址,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; } 
} 
1

執行這個東西做的伎倆:因爲需要一個完整的掃描

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) 
    ) 

不幸的是,查詢速度很慢。

編輯 上述查詢將與ipv4地址一起使用。爲了應對IPv6地址我會用一個CLR標量函數如下所示:

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; 
    } 
} 

然後使用它是這樣的:

DECLARE @ipaddress varbinary(16) = dbo.TryParseIPAddress(N'2001:0db8:85a3:0000:0000:8a2e:0370:7334') 

SELECT * 
FROM CityIp 
WHERE 
    dbo.TryParseIPAddress(IpStart) <= @ipaddress 
    AND dbo.TryParseIPAddress(IpEnd) >= @ipaddress 

編輯: 一兩件事,將加快東西是加入兩個二進制以二進制格式存儲IpStart和IpEnd的計算列。但是,最大的性能改進是如圖所示應用Relational Interval Tree在下面的文章:

Interval Queries in SQL Server

+0

該技巧是否可以與ipv6地址一起使用? –

+0

顯然不是,對不起 –

+0

他們是ipv6地址嗎? –