sql - 使用sql在现有整数子集中不存在的范围内查找下一个可用整数

标签 sql math sql-server-2012 network-programming range

问题陈述:

given a range x -> y of unsigned integers
where x and y are both in the range 0 -> 2n
and n is 0 -> 32 (or 64 in alternate cases)
find the minimum available value
not equal to x or y
that is not in an existing set
where existing sets are arbitrary subsets of x -> y



我正在对数据库中的 IPv4 和 IPv6 子网进行建模。每个子网由其起始地址和结束地址定义(我通过业务规则确保范围的完整性)。因为 IPv6 太大,无法存储在 bigint数据类型我们将 IP 地址存储为 binary(4)binary(16) .

关联的数据存储在 subnet , dhcp_rangeip_address表:
  • 子网 :
    子网范围由开始和结束 IP 地址定义并存储在 subnet 中。 table 。子网范围的大小始终为 2n(根据 CIDR/网络掩码的定义)。
  • 知识产权 :
    一个子网有 0..* IP地址存储在ip_address table 。 IP 地址必须介于起始地址和结束地址之间,但不等于其关联子网定义的范围。
  • DHCP 范围 :
    一个子网有 0..* DHCP 范围存储在 dhcp_range 中 table 。类似于子网,每个 DHCP 范围都定义了一个开始地址和结束地址。 DHCP 范围受相关子网范围的限制。 DHCP 范围彼此不重叠。

  • 我要确定的是子网的下一个可用 IP:
  • 尚未分配的(不在 IP 地址表中)
  • 不在 DHCP 范围内
  • 并且不等于子网范围的开始或结束地址。

  • 我正在寻找一种可以找到最小可用地址或所有可用地址的解决方案。

    我最初的想法是生成由子网范围绑定(bind)的可能地址(数字)的范围,然后根据使用的集合删除地址:
    declare @subnet_sk int = 42
    
    ;with
    address_range as (
        select cast(ipv4_begin as bigint) as available_address
              ,cast(ipv4_end as bigint) as end_address, subnet_sk
          from subnet s
         where subnet_sk = @subnet_sk
    
        union all
    
        select available_address + 1, end_address, subnet_sk
          from address_range
         where available_address + 1 <= end_address
    ),
    assigned_addresses as (
        select ip.[address]
              ,subnet_sk
          from ip_address ip
         where ip.subnet_sk = @subnet_sk
           and ip.address_family = 'InterNetwork'),
    dhcp_ranges as (
        select dhcp.begin_address
              ,dhcp.end_address
              ,subnet_sk
          from dhcp_range dhcp
         where dhcp.subnet_sk = @subnet_sk
           and dhcp.address_family = 'InterNetwork')
    select distinct ar.available_address
      from address_range ar
           join dhcp_ranges dhcp
             on ar.available_address
                not between dhcp.begin_address
                        and dhcp.end_address
           left join assigned_addresses aa
             on ar.available_address = aa.[address]
           join subnet s
             on ar.available_address != s.ipv4_begin
            and ar.available_address != s.ipv4_end
     where aa.[address] is null
       and s.subnet_sk = @subnet_sk
    order by available_address
    option (MAXRECURSION 32767)
    

    上述查询使用递归 CTE,不适用于所有数据排列。递归 CTE 很麻烦,因为它的最大大小限制为 32,767(远小于潜在的范围大小),并且很可能非常慢。我可能可以克服递归 CTE 的问题,但查询在以下情况下失败:
  • 当没有分配 IP 地址或 DHCP 范围时:它不返回任何内容
    应返回子网范围
  • 定义的所有 IP 地址
  • 当分配多个 DHCP 范围时:返回 DHCP 范围内的 IP

  • 为了帮助解决问题,我创建了一个 SQL Fiddle具有三个子网;每个都有不同的特征:切碎、空的或大部分是连续的。上面的查询和 fiddle 中的设置都适用于大部分连续的子网,但不适用于其他子网。还有一个GitHub Gist of the schema and example data .

    我已经努力用递归和堆叠 CTE 生成数字序列,但如上所述,恐怕它们的性能会很差,并且在递归 CTE 的情况下会人为地限制。 Aaron Bertrand在他的系列中详细介绍了 CTE 的一些替代方案 Generate a set or sequence without loops .遗憾的是,数据集对于数字表来说太大了,因为仅为 IPv4 地址空间创建一个需要 32 GB 的磁盘空间(SQL Server 存储 bigint values in 8 bytes)。我更喜欢动态生成序列,但还没有想出一个好的方法。

    或者,我试图通过查看我知道的使用地址来为我的查询设定种子:
    declare @subnet_sk int = 1
    
    select unassigned_range.*
      from (select cast(l.address as bigint) + 1 as start
                  ,min(cast(fr.address as bigint)) - 1 as stop
              from ip_address as l
                   left join ip_address as r on l.address = r.address - 1
                   left join ip_address as fr on l.address < fr.address
             where r.address is null and fr.address is not null
               and l.subnet_sk = @subnet_sk
            group by l.address, r.address) as unassigned_range
           join dhcp_range dhcp
             on unassigned_range.start
                not between cast(dhcp.begin_address as bigint)
                    and cast(dhcp.end_address as bigint)
            and unassigned_range.stop
                not between cast(dhcp.begin_address as bigint)
                    and cast(dhcp.end_address as bigint)
     where dhcp.subnet_sk = @subnet_sk
    

    遗憾的是,当 ip_address 中没有任何内容时,上述查询不起作用。或 dhcp_range表。更糟糕的是,它不知道子网范围的边界 dhcp_range接近子网范围的上限将人为地限制返回的内容,因为查询无法从边缘的空白空间返回行。表现也不算突出。

    使用 SQL 或 TSQL 如何确定受其他范围限制的任意整数范围内的下一个最小可用整数值?

    最佳答案

    在这种情况下不需要递归,因为我们有 LEAD功能。

    我会从“差距”和“孤岛”的角度来思考问题。

    我将首先关注 IPv4,因为用它们做算术更容易,但 IPv6 的想法是相同的,最后我将展示一个通用的解决方案。

    首先,我们有一系列可能的 IP:从 0x000000000xFFFFFFFF .

    在此范围内,存在由 dhcp_range 中的范围(含)定义的“孤岛”。 :dhcp_range.begin_address, dhcp_range.end_address .您可以将分配的 IP 地址列表视为另一组孤岛,每个孤岛都有一个元素:ip_address.address, ip_address.address .最后,子网本身是两个孤岛:0x00000000, subnet.ipv4_beginsubnet.ipv4_end, 0xFFFFFFFF .

    我们知道这些岛屿做不是 重叠,这让我们的生活更轻松。岛屿可以完全相邻。例如,当您有几个连续分配的 IP 地址时,它们之间的差距为零。
    在所有这些岛中,我们需要找到第一个间隙,它至少有一个元素,即非零间隙,即下一个岛在前一个岛结束后的某个距离处开始。

    因此,我们将使用 UNION 将所有岛屿放在一起( CTE_Islands ) 然后按照 end_address 的顺序遍历所有这些(或 begin_address ,使用具有索引的字段)并使用 LEAD向前看并获得下一个岛屿的起始地址。最后我们将有一个表格,其中每一行都有 end_address当前岛屿和 begin_address下一个岛屿 ( CTE_Diff )。如果它们之间的差异大于一,则表示“间隙”足够宽,我们将返回 end_address当前岛屿的加1。

    给定子网的第一个可用 IP 地址

    DECLARE @ParamSubnet_sk int = 1;
    
    WITH
    CTE_Islands
    AS
    (
        SELECT CAST(begin_address AS bigint) AS begin_address, CAST(end_address AS bigint) AS end_address
        FROM dhcp_range
        WHERE subnet_sk = @ParamSubnet_sk
    
        UNION ALL
    
        SELECT CAST(address AS bigint) AS begin_address, CAST(address AS bigint) AS end_address
        FROM ip_address
        WHERE subnet_sk = @ParamSubnet_sk
    
        UNION ALL
    
        SELECT CAST(0x00000000 AS bigint) AS begin_address, CAST(ipv4_begin AS bigint) AS end_address
        FROM subnet
        WHERE subnet_sk = @ParamSubnet_sk
    
        UNION ALL
    
        SELECT CAST(ipv4_end AS bigint) AS begin_address, CAST(0xFFFFFFFF AS bigint) AS end_address
        FROM subnet
        WHERE subnet_sk = @ParamSubnet_sk
    )
    ,CTE_Diff
    AS
    (
        SELECT
            begin_address
            , end_address
            --, LEAD(begin_address) OVER(ORDER BY end_address) AS BeginNextIsland
            , LEAD(begin_address) OVER(ORDER BY end_address) - end_address AS Diff
        FROM CTE_Islands
    )
    SELECT TOP(1)
        CAST(end_address + 1 AS varbinary(4)) AS NextAvailableIPAddress
    FROM CTE_Diff
    WHERE Diff > 1
    ORDER BY end_address;
    

    如果至少有一个 IP 地址可用,则结果集将包含一行;如果没有可用的 IP 地址,则结果集将根本不包含行。
    For parameter 1 result is `0xAC101129`.
    For parameter 2 result is `0xC0A81B1F`.
    For parameter 3 result is `0xC0A8160C`.
    

    这是指向 SQLFiddle 的链接.它不适用于参数,所以我硬编码 1那里。在 UNION 中将其更改为其他子网 ID(2 或 3)以尝试其他子网。此外,它没有在 varbinary 中显示结果正确,所以我将其保留为 bigint。使用,例如,windows 计算器将其转换为十六进制以验证结果。

    如果您不通过 TOP(1) 将结果限制在第一个间隔内,您将获得所有可用 IP 范围(间隙)的列表。

    给定子网的所有可用 IP 地址范围列表
    DECLARE @ParamSubnet_sk int = 1;
    
    WITH
    CTE_Islands
    AS
    (
        SELECT CAST(begin_address AS bigint) AS begin_address, CAST(end_address AS bigint) AS end_address
        FROM dhcp_range
        WHERE subnet_sk = @ParamSubnet_sk
    
        UNION ALL
    
        SELECT CAST(address AS bigint) AS begin_address, CAST(address AS bigint) AS end_address
        FROM ip_address
        WHERE subnet_sk = @ParamSubnet_sk
    
        UNION ALL
    
        SELECT CAST(0x00000000 AS bigint) AS begin_address, CAST(ipv4_begin AS bigint) AS end_address
        FROM subnet
        WHERE subnet_sk = @ParamSubnet_sk
    
        UNION ALL
    
        SELECT CAST(ipv4_end AS bigint) AS begin_address, CAST(0xFFFFFFFF AS bigint) AS end_address
        FROM subnet
        WHERE subnet_sk = @ParamSubnet_sk
    )
    ,CTE_Diff
    AS
    (
        SELECT
            begin_address
            , end_address
            , LEAD(begin_address) OVER(ORDER BY end_address) AS BeginNextIsland
            , LEAD(begin_address) OVER(ORDER BY end_address) - end_address AS Diff
        FROM CTE_Islands
    )
    SELECT
        CAST(end_address + 1 AS varbinary(4)) AS begin_range_AvailableIPAddress
        ,CAST(BeginNextIsland - 1 AS varbinary(4)) AS end_range_AvailableIPAddress
    FROM CTE_Diff
    WHERE Diff > 1
    ORDER BY end_address;
    

    结果。 SQL Fiddle结果为简单的 bigint,不是十六进制的,并且带有硬编码的参数 ID。
    Result set for ID = 1
    begin_range_AvailableIPAddress    end_range_AvailableIPAddress
    0xAC101129                        0xAC10112E
    
    Result set for ID = 2
    begin_range_AvailableIPAddress    end_range_AvailableIPAddress
    0xC0A81B1F                        0xC0A81B1F
    0xC0A81B22                        0xC0A81B28
    0xC0A81BFA                        0xC0A81BFE
    
    Result set for ID = 3
    begin_range_AvailableIPAddress    end_range_AvailableIPAddress
    0xC0A8160C                        0xC0A8160C
    0xC0A816FE                        0xC0A816FE
    

    每个子网的第一个可用 IP 地址

    扩展查询并返回所有子网的第一个可用 IP 地址很容易,而不是指定一个特定的子网。使用 CROSS APPLY获取每个子网的岛屿列表,然后添加 PARTITION BY subnet_skLEAD功能。
    WITH
    CTE_Islands
    AS
    (
        SELECT
            subnet_sk
            , begin_address
            , end_address
        FROM
            subnet AS Main
            CROSS APPLY
            (
                SELECT CAST(begin_address AS bigint) AS begin_address, CAST(end_address AS bigint) AS end_address
                FROM dhcp_range
                WHERE dhcp_range.subnet_sk = Main.subnet_sk
    
                UNION ALL
    
                SELECT CAST(address AS bigint) AS begin_address, CAST(address AS bigint) AS end_address
                FROM ip_address
                WHERE ip_address.subnet_sk = Main.subnet_sk
    
                UNION ALL
    
                SELECT CAST(0x00000000 AS bigint) AS begin_address, CAST(ipv4_begin AS bigint) AS end_address
                FROM subnet
                WHERE subnet.subnet_sk = Main.subnet_sk
    
                UNION ALL
    
                SELECT CAST(ipv4_end AS bigint) AS begin_address, CAST(0xFFFFFFFF AS bigint) AS end_address
                FROM subnet
                WHERE subnet.subnet_sk = Main.subnet_sk
            ) AS CA
    )
    ,CTE_Diff
    AS
    (
        SELECT
            subnet_sk
            , begin_address
            , end_address
            , LEAD(begin_address) OVER(PARTITION BY subnet_sk ORDER BY end_address) - end_address AS Diff
        FROM CTE_Islands
    )
    SELECT
        subnet_sk
        , CAST(MIN(end_address) + 1 as varbinary(4)) AS NextAvailableIPAddress
    FROM CTE_Diff
    WHERE Diff > 1
    GROUP BY subnet_sk
    

    结果集
    subnet_sk    NextAvailableIPAddress
    1            0xAC101129
    2            0xC0A81B1F
    3            0xC0A8160C
    

    这是SQLFiddle .我不得不删除转换为 varbinary在 SQL Fiddle 中,因为它显示的结果不正确。

    IPv4 和 IPv6 的通用解决方案

    所有子网的所有可用 IP 地址范围

    SQL Fiddle with sample IPv4 and IPv6 data, functions and final query

    您的 IPv6 示例数据不太正确 - 子网结束 0xFC00000000000000FFFFFFFFFFFFFFFF小于您的 dhcp 范围,所以我将其更改为 0xFC0001066800000000000000FFFFFFFF .此外,您在同一子网中同时拥有 IPv4 和 IPv6,处理起来很麻烦。为了这个例子,我稍微改变了你的模式 - 而不是显式 ipv4_begin / endipv6_begin / endsubnet我刚做的 ip_begin / endvarbinary(16) (与您的其他表相同)。我也删除了 address_family ,否则它对于 SQL Fiddle 来说太大了。

    算术函数

    为了使其适用于 IPv6,我们需要弄清楚如何添加/减去 1往/返 binary(16) .我会为它制作 CLR 功能。如果不允许启用 CLR,则可以通过标准 T-SQL 进行。我创建了两个返回表而不是标量的函数,因为这样它们可以被优化器内联。我想做一个通用的解决方案,所以函数会接受 varbinary(16)并适用于 IPv4 和 IPv6。

    这是用于递增 varbinary(16) 的 T-SQL 函数由一个。如果参数不是 16 字节长,我假设它是 IPv4 并简单地将其转换为 bigint添加 1然后回到 binary .否则,我 split binary(16)分成两部分,每部分 8 个字节,然后将它们转换为 bigint . bigint是有符号的,但我们需要无符号增量,所以我们需要检查一些情况。
    else部分是最常见的 - 我们只是将低部分加一并将结果附加到原始高部分。

    如果低部分是0xFFFFFFFFFFFFFFFF ,然后我们将低部分设置为 0x0000000000000000并结转标志,即将高部分加一。

    如果低部分是0x7FFFFFFFFFFFFFFF ,然后我们将低部分设置为 0x8000000000000000明确地,因为试图增加这个 bigint value 会导致溢出。

    如果整数是0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF我们将结果设置为 0x00000000000000000000000000000000 .

    减一的功能类似。
    CREATE FUNCTION [dbo].[BinaryInc](@src varbinary(16))
    RETURNS TABLE AS
    RETURN
        SELECT
        CASE WHEN DATALENGTH(@src) = 16
        THEN
            -- Increment IPv6 by splitting it into two bigints 8 bytes each and then concatenating them
            CASE
            WHEN @src = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
            THEN 0x00000000000000000000000000000000
    
            WHEN SUBSTRING(@src, 9, 8) = 0x7FFFFFFFFFFFFFFF
            THEN SUBSTRING(@src, 1, 8) + 0x8000000000000000
    
            WHEN SUBSTRING(@src, 9, 8) = 0xFFFFFFFFFFFFFFFF
            THEN CAST(CAST(SUBSTRING(@src, 1, 8) AS bigint) + 1 AS binary(8)) + 0x0000000000000000
    
            ELSE SUBSTRING(@src, 1, 8) + CAST(CAST(SUBSTRING(@src, 9, 8) AS bigint) + 1 AS binary(8))
            END
        ELSE
            -- Increment IPv4 by converting it into 8 byte bigint and then back into 4 bytes binary
            CAST(CAST(CAST(@src AS bigint) + 1 AS binary(4)) AS varbinary(16))
        END AS Result
        ;
    GO
    
    CREATE FUNCTION [dbo].[BinaryDec](@src varbinary(16))
    RETURNS TABLE AS
    RETURN
        SELECT
        CASE WHEN DATALENGTH(@src) = 16
        THEN
            -- Decrement IPv6 by splitting it into two bigints 8 bytes each and then concatenating them
            CASE
            WHEN @src = 0x00000000000000000000000000000000
            THEN 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    
            WHEN SUBSTRING(@src, 9, 8) = 0x8000000000000000
            THEN SUBSTRING(@src, 1, 8) + 0x7FFFFFFFFFFFFFFF
    
            WHEN SUBSTRING(@src, 9, 8) = 0x0000000000000000
            THEN CAST(CAST(SUBSTRING(@src, 1, 8) AS bigint) - 1 AS binary(8)) + 0xFFFFFFFFFFFFFFFF
    
            ELSE SUBSTRING(@src, 1, 8) + CAST(CAST(SUBSTRING(@src, 9, 8) AS bigint) - 1 AS binary(8))
            END
        ELSE
            -- Decrement IPv4 by converting it into 8 byte bigint and then back into 4 bytes binary
            CAST(CAST(CAST(@src AS bigint) - 1 AS binary(4)) AS varbinary(16))
        END AS Result
        ;
    GO
    

    所有子网的所有可用 IP 地址范围
    WITH
    CTE_Islands
    AS
    (
        SELECT subnet_sk, begin_address, end_address
        FROM dhcp_range
    
        UNION ALL
    
        SELECT subnet_sk, address AS begin_address, address AS end_address
        FROM ip_address
    
        UNION ALL
    
        SELECT subnet_sk, SUBSTRING(0x00000000000000000000000000000000, 1, DATALENGTH(ip_begin)) AS begin_address, ip_begin AS end_address
        FROM subnet
    
        UNION ALL
    
        SELECT subnet_sk, ip_end AS begin_address, SUBSTRING(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, 1, DATALENGTH(ip_end)) AS end_address
        FROM subnet
    )
    ,CTE_Gaps
    AS
    (
        SELECT
            subnet_sk
            ,end_address AS EndThisIsland
            ,LEAD(begin_address) OVER(PARTITION BY subnet_sk ORDER BY end_address) AS BeginNextIsland
        FROM CTE_Islands
    )
    ,CTE_GapsIncDec
    AS
    (
        SELECT
            subnet_sk
            ,EndThisIsland
            ,EndThisIslandInc
            ,BeginNextIslandDec
            ,BeginNextIsland
        FROM CTE_Gaps
            CROSS APPLY
            (
                SELECT bi.Result AS EndThisIslandInc
                FROM dbo.BinaryInc(EndThisIsland) AS bi
            ) AS CA_Inc
            CROSS APPLY
            (
                SELECT bd.Result AS BeginNextIslandDec
                FROM dbo.BinaryDec(BeginNextIsland) AS bd
            ) AS CA_Dec
    )
    SELECT
        subnet_sk
        ,EndThisIslandInc AS begin_range_AvailableIPAddress
        ,BeginNextIslandDec AS end_range_AvailableIPAddress
    FROM CTE_GapsIncDec
    WHERE CTE_GapsIncDec.EndThisIslandInc <> BeginNextIsland
    ORDER BY subnet_sk, EndThisIsland;
    

    结果集
    subnet_sk    begin_range_AvailableIPAddress        end_range_AvailableIPAddress
    1            0xAC101129                            0xAC10112E
    2            0xC0A81B1F                            0xC0A81B1F
    2            0xC0A81B22                            0xC0A81B28
    2            0xC0A81BFA                            0xC0A81BFE
    3            0xC0A8160C                            0xC0A8160C
    3            0xC0A816FE                            0xC0A816FE
    4            0xFC000000000000000000000000000001    0xFC0000000000000000000000000000FF
    4            0xFC000000000000000000000000000101    0xFC0000000000000000000000000001FF
    4            0xFC000000000000000000000000000201    0xFC0000000000000000000000000002FF
    4            0xFC000000000000000000000000000301    0xFC0000000000000000000000000003FF
    4            0xFC000000000000000000000000000401    0xFC0000000000000000000000000004FF
    4            0xFC000000000000000000000000000501    0xFC0000000000000000000000000005FF
    4            0xFC000000000000000000000000000601    0xFC0000000000000000000000000006FF
    4            0xFC000000000000000000000000000701    0xFC0000000000000000000000000007FF
    4            0xFC000000000000000000000000000801    0xFC0000000000000000000000000008FF
    4            0xFC000000000000000000000000000901    0xFC00000000000000BFFFFFFFFFFFFFFD
    4            0xFC00000000000000BFFFFFFFFFFFFFFF    0xFC00000000000000CFFFFFFFFFFFFFFD
    4            0xFC00000000000000CFFFFFFFFFFFFFFF    0xFC00000000000000FBFFFFFFFFFFFFFD
    4            0xFC00000000000000FBFFFFFFFFFFFFFF    0xFC00000000000000FCFFFFFFFFFFFFFD
    4            0xFC00000000000000FCFFFFFFFFFFFFFF    0xFC00000000000000FFBFFFFFFFFFFFFD
    4            0xFC00000000000000FFBFFFFFFFFFFFFF    0xFC00000000000000FFCFFFFFFFFFFFFD
    4            0xFC00000000000000FFCFFFFFFFFFFFFF    0xFC00000000000000FFFBFFFFFFFFFFFD
    4            0xFC00000000000000FFFBFFFFFFFFFFFF    0xFC00000000000000FFFCFFFFFFFFFFFD
    4            0xFC00000000000000FFFCFFFFFFFFFFFF    0xFC00000000000000FFFFBFFFFFFFFFFD
    4            0xFC00000000000000FFFFBFFFFFFFFFFF    0xFC00000000000000FFFFCFFFFFFFFFFD
    4            0xFC00000000000000FFFFCFFFFFFFFFFF    0xFC00000000000000FFFFFBFFFFFFFFFD
    4            0xFC00000000000000FFFFFBFFFFFFFFFF    0xFC00000000000000FFFFFCFFFFFFFFFD
    4            0xFC00000000000000FFFFFCFFFFFFFFFF    0xFC00000000000000FFFFFFBFFFFFFFFD
    4            0xFC00000000000000FFFFFFBFFFFFFFFF    0xFC00000000000000FFFFFFCFFFFFFFFD
    4            0xFC00000000000000FFFFFFCFFFFFFFFF    0xFC00000000000000FFFFFFFBFFFFFFFD
    4            0xFC00000000000000FFFFFFFBFFFFFFFF    0xFC00000000000000FFFFFFFCFFFFFFFD
    4            0xFC00000000000000FFFFFFFCFFFFFFFF    0xFC00000000000000FFFFFFFFBFFFFFFD
    4            0xFC00000000000000FFFFFFFFBFFFFFFF    0xFC00000000000000FFFFFFFFCFFFFFFD
    4            0xFC00000000000000FFFFFFFFCFFFFFFF    0xFC00000000000000FFFFFFFFFBFFFFFD
    4            0xFC00000000000000FFFFFFFFFBFFFFFF    0xFC00000000000000FFFFFFFFFCFFFFFD
    4            0xFC00000000000000FFFFFFFFFCFFFFFF    0xFC00000000000000FFFFFFFFFFBFFFFD
    4            0xFC00000000000000FFFFFFFFFFBFFFFF    0xFC00000000000000FFFFFFFFFFCFFFFD
    4            0xFC00000000000000FFFFFFFFFFCFFFFF    0xFC00000000000000FFFFFFFFFFFBFFFD
    4            0xFC00000000000000FFFFFFFFFFFBFFFF    0xFC00000000000000FFFFFFFFFFFCFFFD
    4            0xFC00000000000000FFFFFFFFFFFCFFFF    0xFC00000000000000FFFFFFFFFFFFBFFD
    4            0xFC00000000000000FFFFFFFFFFFFBFFF    0xFC00000000000000FFFFFFFFFFFFCFFD
    4            0xFC00000000000000FFFFFFFFFFFFCFFF    0xFC00000000000000FFFFFFFFFFFFFBFD
    4            0xFC00000000000000FFFFFFFFFFFFFBFF    0xFC00000000000000FFFFFFFFFFFFFCFD
    4            0xFC00000000000000FFFFFFFFFFFFFCFF    0xFC00000000000000FFFFFFFFFFFFFFBD
    4            0xFC00000000000000FFFFFFFFFFFFFFBF    0xFC00000000000000FFFFFFFFFFFFFFCD
    4            0xFC00000000000000FFFFFFFFFFFFFFCF    0xFC0001065FFFFFFFFFFFFFFFFFFFFFFF
    4            0xFC000106600000000000000100000000    0xFC00010666FFFFFFFFFFFFFFFFFFFFFF
    4            0xFC000106670000000000000100000000    0xFC000106677FFFFFFFFFFFFFFFFFFFFF
    4            0xFC000106678000000000000100000000    0xFC000106678FFFFFFFFFFFFFFFFFFFFF
    4            0xFC000106679000000000000100000000    0xFC0001066800000000000000FFFFFFFE
    

    执行计划

    我很好奇这里建议的不同解决方案是如何工作的,所以我查看了他们的执行计划。请记住,这些计划适用于没有任何索引的小样本数据集。

    我的 IPv4 和 IPv6 通用解决方案:



    的类似解决方案dnoeth :



    来自 的解决方案查不使用 LEAD功能:

    关于sql - 使用sql在现有整数子集中不存在的范围内查找下一个可用整数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28844504/

    相关文章:

    sql-server - 随机删除表中 75% 的内容

    math - 数学与编程语言的融合

    java - 从文件中读取数字并检查随机生成

    sql - 让 AVG() 忽略 0?

    java - sql insert语句设置行成比例

    c# - 看着球体的相机 - 极包裹

    通过 SQL Server 2012 备份和恢复文件 FileTable

    sql - 是否可以从键/值对更新行?

    sql - 日期范围内的分组/聚合

    python - MySQL 从 Python 中的 CSV 字段中选择