我正在尝试搜索 IPGeolocation 数据,该数据由数据供应商提供为 IPv4 和 IPv6 的开始和结束范围。但是,当我转换提供的 IP 时,我得到的结果不正确。
为了按范围搜索,我将VARCHAR(39)
IPv6 的表示形式转换为VARBINARY
(即十六进制),然后使用BETWEEN
运算符查看搜索到的十六进制值是否在特定范围内。
查询逻辑:
SELECT CONVERT(VARBINARY(16), '2600:8800:6a06:2000:1d29:3b7:8c7c:271b')
搜索:
IP: 2600:8800:6a06:2000:1d29:3b7:8c7c:271b
IpHex: 0x323630303A383830303A366130363A32
结果 1
启动:2600:8800:490:0:0:0:0:0
IpStartHex: 0x323630303A383830303A3439303A303A
IpEnd: 2600:8800:77f:ffff:ffff:ffff:ffff:ffff
IpEndHex: 0x323630303A383830303A3737663A6666
结果 2
启动:2600:8800:6a06:1d01:0:0:0:0
IpStartHex: 0x323630303A383830303A366130363A31
IpEnd: 2600:8800:6a06:24ff:ffff:ffff:ffff:ffff
IpEndHex: 0x323630303A383830303A366130363A32
最初您的转换有两个问题:
这是错误的转换“类型”。您正在将字符串转换为字符本身的十六进制/二进制表示形式。例如,“26”在十六进制中将变为 0x3236,因为 0x32 与 doing 相同
CHAR(0x32)
,0x36 与 相同CHAR(0x36)
,或者它可以称为数字“2”的 ASCII 值。你想要的是简单地将字符串“26”(代表一个字节)的形式更改为0x26
. 为此,您需要为CONVERT
函数使用样式编号“2”。例如:这就是你
0x323630303A383830303A366130363A32
从2600:8800:6a06:2000:1d29:3b7:8c7c:271b
. 您实际上只从该字符串中获取前 16 个字符,它们是:2600:8800:6a06:2
。左边的两个字我上面直接解释了。右侧的 3 个字符6:2
是0x363A32
十六进制值的右侧,因为0x36
=“6”、0x3A
=“:”和0x32
=“2”。您需要去掉冒号,因为它们不是有效的二进制数字。但是冒号作为分隔符存在是有原因的:有 8 个段,如果删除前导“0”(这是有效的),那么 0 确实需要是
0x0000
,而不是0x00
or0x0
。因此,您需要确保每个段返回为 4 个十六进制数字。您可以通过以下方式完成所有这些:
BINARY(2)
如果您使用的是 SQL Server 2016 或更新版本,则可以使用内置
STRING_SPLIT
函数。或者,如果您使用的是 SQL Server 2016 之前的版本,则可以使用已有的任何拆分器,也可以使用我的SQL# SQLCLR 库的免费版本(免费版中有多个字符串拆分器)。例如:
回报:
那只是一种查看碎片是什么的方法。要将它们组合成一个十六进制值,您可以执行以下操作:
您可以创建一个函数来处理此转换。然后您可以存储这些
VARBINARY(16)
值并为它们创建索引,然后转换传入的字符串并将其存储在变量中以在查询中使用。这是相同的方法,但使用内置
STRING_SPLIT
函数:请记住,有些人
STRING_SPLIT
在这种情况下犹豫不决,因为这里返回拆分部分的顺序很重要,因为如果它们以不同的顺序返回,它可以更改值,而内置函数不会' 返回行号。我个人看不出返回值是如何乱序返回的,但这只是一个有根据的猜测,并不能保证。如果你想要保证,那就不要使用STRING_SPLIT
内置函数。PS上面显示的解决方案(即在冒号上拆分)不处理“简化”符号/语法,其中“::”可以替换一个或多个连续范围的“0000”。例如:
2600:100:400::0
。因此,该解决方案要求所有 8 个段都至少包含“0”。PPS如果您在应用程序或 SQLCLR 或 SSIS 中使用 .NET,则可以结合使用IPAddress.TryParse和IPAddress.GetAddressBytes来处理简化的“::”表示法。或者,您总是可以编写一堆代码来用纯 T-SQL 处理它。
PPPS
INET_IPv6toBinary
将在SQL#的下一个版本(4.3 版)中可用:-)