我正在对数据库中网络的各个方面进行建模。我们正在处理的一个更烦人的问题是创建子网范围,然后确定一组给定的 IP 是否在这些范围内。我们当前的模型通过以下列说明了 IPv4 和 IPv6 之间的差异:
[subnet_sk] [int] IDENTITY(1,1) NOT NULL,
[ipv6_network] [char](39) NULL,
[ipv6_broadcast] [char](39) NULL,
[ipv4_network] [char](15) NULL,
[ipv4_broadcast] [char](15) NULL,
[network_type] [char](4) NOT NULL
上面的模式做了一些重要的假设。我们正在使用完全扩展的 IP ( 192.168.001.001
vs. 192.168.1.1
) 进行存储和比较。我们做出这个决定是因为在 SQL Server 中以数字方式存储 IPv6 地址的问题(bigint 是无符号的,这意味着我们必须使用六列来表示 IPv6)。
鉴于此表模式,很容易编写一个选择语句来确定任一类型的 IP 是否在表中的范围之间:
select *
from subnet
where '1234:0000:0000:0000:fc12:00ab:0042:1050'
between ipv6_network
and ipv6_broadcast
-- or alternatively for IPv4
select *
from subnet
where '192.168.005.015'
between ipv4_network
and ipv4_broadcast
更难的是给出一个 IP 列表来确定其中哪些在子网范围之间。IP 列表将由用户输入提供,不会存储在数据库中。显然,对于存储在数据库中的数据,我可以进行类似的连接,如下例所示。
例如,用户可以1234:0000:0000:0000:fc12:00ab:0042:1050
提供192.168.001.001
和192.168.1.1
。我想出的唯一解决方案是使用表值函数来拆分IP 列表并使用 between 执行连接:
-- this only covers the IPv4 addresses from the above list a similar query would
-- be used for IPv6 and the two queries could be unioned
select sub.*
from fn_SplitList('192.168.001.001,192.168.005.015',',') split
join subnet sub
on split.Data
between sub.ipv4_network
and sub.ipv4_broadcast
虽然使用拆分功能可以工作,但感觉很hacky。我花了大部分时间在common table expressions周围嗅探,但想不出一个可行的实现。理想情况下,单个选择将确定是否从 IPv4 或 IPv6 列中反弹给定的字符串,但如果这不可能,我可以在将 IP 集合交给数据库之前分离列表。
为了更容易回答,我创建了上面的SQL Fiddle。SQL 中是否有一种机制(我宁愿不使用 T-SQL)给定 IP 列表来确定这些 IP 位于哪些现有子网范围?上述模式是否是解决问题的正确方法,不同的数据模型会导致更简单的解决方案吗?