With sql find next available integer within range that is not present in existing integer subset(s)

后端 未结 4 1145
谎友^
谎友^ 2021-02-07 12:02

Problem statement:

given a range x -> y of unsigned integers
where x and y are both in the range

4条回答
  •  别跟我提以往
    2021-02-07 12:20

    This is a kind of question I usually try to solve with a simple cumulative sum over +1/-1.

    ip_address: ip is not available for ip_address, but available starting with ip_address + 1

    subnet: ip is not available for ipv4_end, but available stating with ipv4_begin + 1

    dhcp_range: ip is not available after begin_address, but available starting with end_address + 1

    Now sum all the +1/-1 ordered by ip addresses, whenever it's greater than zero it's the start of a range of free tips and now the next row's ip is the start of a used range.

    SELECT
       subnet_sk
      ,ip_begin
      ,ip_end
    FROM
     (
       SELECT
          subnet_sk
         ,ip AS ip_begin
        -- ,x
         ,LEAD(ip)
          OVER (ORDER BY ip, x) - 1 AS ip_end
         ,SUM(x)
          OVER (ORDER BY ip, x 
                ROWS UNBOUNDED PRECEDING) AS avail
       FROM
       (
          SELECT
             subnet_sk, CAST(ipv4_begin AS BIGINT)+1 AS ip, 1 AS x 
          FROM subnet
       --   WHERE subnet_sk = 1
    
          UNION ALL
    
          SELECT
             subnet_sk, CAST(ipv4_end AS BIGINT), -1 
          FROM subnet
       --   WHERE subnet_sk = 1
    
          UNION ALL
    
          SELECT
             subnet_sk, CAST(begin_address AS BIGINT), -1
          FROM dhcp_range
       --   WHERE subnet_sk = 1
    
          UNION ALL
    
          SELECT
             subnet_sk, CAST(end_address AS BIGINT)+1, 1 
          FROM dhcp_range
       --   WHERE subnet_sk = 1
    
          UNION ALL
    
          SELECT
             subnet_sk, CAST(address AS BIGINT), -1 
          FROM ip_address
       --   WHERE subnet_sk = 1
    
          UNION ALL
    
          SELECT
             subnet_sk, CAST(address AS BIGINT)+1, 1 
          FROM ip_address
       --   WHERE subnet_sk = 1
       ) AS dt
     ) AS dt
    WHERE avail > 0
    

    This will return all available ranges, for a single subnet simply uncomment the WHERE-condition: fiddle

提交回复
热议问题