Problem statement:
given a range
x -> y
of unsigned integers
wherex
andy
are both in the range
After a lot of thinking, I believe a query as simple as this will do:
with a as(
-- next ip address
select n.next_address, i.subnet_sk
from ip_address i
CROSS APPLY (SELECT convert(binary(4), convert(bigint, i.address) + 1) AS next_address) as n
where n.next_address NOT IN (SELECT address FROM ip_address)
AND EXISTS (SELECT 1 FROM subnet s WHERE s.subnet_sk = i.subnet_sk and n.next_address > s.ipv4_begin and n.next_address < s.ipv4_end)
UNION -- use UNION here, not UNION ALL to remove duplicates
-- first ip address for completely unassigned subnets
SELECT next_address, subnet_sk
FROM subnet
CROSS APPLY (SELECT convert(binary(4), convert(bigint, ipv4_begin) + 1) AS next_address) n
where n.next_address NOT IN (SELECT address FROM ip_address)
UNION -- use UNION here, not UNION ALL to remove duplicates
-- next ip address from dhcp ranges
SELECT next_address, subnet_sk
FROM dhcp_range
CROSS APPLY (SELECT convert(binary(4), convert(bigint, end_address) + 1) AS next_address) n
where n.next_address NOT IN (SELECT address FROM ip_address)
)
SELECT min(next_address), subnet_sk
FROM a WHERE NOT exists(SELECT 1 FROM dhcp_range dhcp
WHERE a.subnet_sk = dhcp.subnet_sk and a.next_address
between dhcp.begin_address
and dhcp.end_address)
GROUP BY subnet_sk
It is for IPV4, but can be easily extended for IPV6
SQLFiddle
Results for each subnet:
subnet_sk
---------- -----------
0xAC101129 1
0xC0A81B1F 2
0xC0A8160C 3
(3 row(s) affected)
In my opinion it should be very fast. Please check it