我需要执行比较两个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
答案 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,如下文所示:
答案 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; }
}