给定子网范围和IP列表,选择IP介于其间的所有行

ahs*_*ele 9 sql sql-server ipv4 ipv6

我正在为数据库中的网络的各个方面建模.我们正在处理的一个更烦人的问题是创建子网范围,然后确定给定的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
Run Code Online (Sandbox Code Playgroud)

上面的模式做了一些重要的假设,这些假设很重要.我们正在利用完全扩展的IP(192.168.001.001vs. 192.168.1.1)进行存储和比较.我们做出这个决定是因为围绕在SQL服务器中以数字方式存储IPv6地址的问题(bigints是无符号的,这意味着我们必须使用六列来表示IPv6).

给定此表模式,可以很容易地编写一个select语句来确定任一类型的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
Run Code Online (Sandbox Code Playgroud)

更为困难的是,IP列表确定哪些IP位于子网范围之间.IP列表将由用户输入提供,存储在数据库中.显然,对于存储在数据库中的数据,我可以进行类似的连接,如下例所示.

例如,用户可以提供1234:0000:0000:0000:fc12:00ab:0042:1050,192.168.001.001192.168.1.1.我提出的唯一解决方案是使用表值函数来拆分 IP 列表并使用以下之间执行连接:

-- 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
Run Code Online (Sandbox Code Playgroud)

虽然使用分割功能起作用但感觉很糟糕.我花了大部分时间在公共表表达式周围嗅探,但想不出一个可行的实现.理想情况下,单个选择将决定是否从IPv4或IPv6列反弹给定字符串,但如果不可能,我可以在将IP集合关闭到数据库之前分离列表.

为了更容易回答我创建了上面的SQL小提琴.在给定IP列表的情况下,SQL中是否存在一种机制(我不想使用T-SQL)来确定这些IP介于哪个现有子网范围之间?以上架构甚至是解决问题的正确方法,不同的数据模型会导致更简单的解决方案吗?

Chr*_*itz 1

这不是一个完整的解决方案,而更多的是另一种设计的想法,我在想而不是进行典型的 SQL 比较,为什么不尝试使用逻辑比较。对 sql 实现知之甚少,我尝试过按位比较(使用 bigint)

有很多优化要做,但我认为它可能会有所帮助,

一个小演示,我比较 4 个 ip(192.168.1.1 和另外 3 个),我将它们用作 bigint,因为 int 太小,并且我需要使用逻辑按位比较,(更多信息请参见http://msdn.microsoft .com/en-us/library/ms174965.aspx

select * from (
    select cast(192168001001 as bigint) as ip union all
    select cast(192168001002 as bigint) as ip union all
    select cast(192168002001 as bigint) as ip union all
    select cast(192168002002 as bigint) as ip
) as ip_table
where ip & cast(192168001000 as bigint) = cast(192168001000 as bigint)
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,我(AND/&)IP和网络地址,然后我将其与网络地址进行比较,如果匹配,则它属于这个范围

如果我错了,请纠正我,我需要更多地考虑这个,确实非常有趣的东西

结果

编辑:如下所述,bigint 对于 IPv6 来说太小了,所以遗憾的是这不起作用,按位(AND)运算不能用二进制数据类型完成,它只接受整数类型......