Select record between two IP ranges

Viewed 11290

I have a table which stores a ID, Name, Code, IPLow, IPHigh such as:

1, Lucas, 804645, 192.130.1.1, 192.130.1.254
2, Maria, 222255, 192.168.2.1, 192.168.2.254
3, Julia, 123456, 192.150.3.1, 192.150.3.254

Now, if I have an IP address 192.168.2.50, how can I retrieve the matching record?

Edit

Based on Gordon's answer (which I'm getting compilation errors) this is what I have:

select PersonnelPC.*
from (select PersonnelPC.*,
             (
              cast(parsename(iplow, 4)*1000000000 as decimal(12, 0)) +
              cast(parsename(iplow, 3)*1000000 as decimal(12, 0)) +
              cast(parsename(iplow, 2)*1000 as decimal(12, 0)) +
              (parsename(iplow, 1))
             ) as iplow_decimal,
            (
              cast(parsename(iphigh, 4)*1000000000 as decimal(12, 0)) +
              cast(parsename(iphigh, 3)*1000000 as decimal(12, 0)) +
              cast(parsename(iphigh, 2)*1000 as decimal(12, 0)) +
              (parsename(iphigh, 1))
             ) as iphigh_decimal
      from PersonnelPC
     ) PersonnelPC
where 192168002050 between iplow_decimal and iphigh_decimal;

but this gives me an error:

Msg 8115, Level 16, State 2, Line 1
Arithmetic overflow error converting expression to data type int.

Any ideas?

8 Answers
select *
from ip a
join ip_details b
on a.ip_address >= b.ip_start
and a.ip_address <= b.ip_end;

In this, table "a" contains list of IP address and table "b" contains the IP ranges.

Instead of converting the ip address to numeric we can directly compare the string, it will do a byte by byte comparison.

This is working for me(PostgreSQL).

I was thinking along the lines of Gordon's answer, then realized you don't actually need to mess with numbers. If you zero-pad each part of the address, a string comparison works:

DECLARE @search varchar(50) = '192.168.2.50';
WITH DATA AS (
    SELECT * FROM ( values 
            (1, 'Lucas', '192.130.1.1', '192.130.1.254'),
            (2, 'Maria', '192.168.2.1', '192.168.2.254'),
            (3, 'Julia', '192.150.3.1', '192.150.3.254')
    ) AS tbl (ID,Name,IPLow,IPHigh)
)
SELECT *
FROM DATA
WHERE REPLACE(STR(PARSENAME( @search, 4 ), 3, 0), ' ', '0')
    + REPLACE(STR(PARSENAME( @search, 3 ), 3, 0), ' ', '0')
    + REPLACE(STR(PARSENAME( @search, 2 ), 3, 0), ' ', '0')
    + REPLACE(STR(PARSENAME( @search, 1 ), 3, 0), ' ', '0')

    BETWEEN

      REPLACE(STR(PARSENAME( IPLow, 4 ), 3, 0), ' ', '0')
    + REPLACE(STR(PARSENAME( IPLow, 3 ), 3, 0), ' ', '0')
    + REPLACE(STR(PARSENAME( IPLow, 2 ), 3, 0), ' ', '0')
    + REPLACE(STR(PARSENAME( IPLow, 1 ), 3, 0), ' ', '0')

    AND

      REPLACE(STR(PARSENAME( IPHigh, 4 ), 3, 0), ' ', '0')
    + REPLACE(STR(PARSENAME( IPHigh, 3 ), 3, 0), ' ', '0')
    + REPLACE(STR(PARSENAME( IPHigh, 2 ), 3, 0), ' ', '0')
    + REPLACE(STR(PARSENAME( IPHigh, 1 ), 3, 0), ' ', '0')

You can, of course, put this inside a UDF for simplicity, though watch out for the performance hit on large queries.

CREATE FUNCTION dbo.IP_Comparable(@IP varchar(50))
RETURNS varchar(50)
WITH SCHEMABINDING
BEGIN
    RETURN REPLACE(STR(PARSENAME( @IP, 4 ), 3, 0), ' ', '0')
         + REPLACE(STR(PARSENAME( @IP, 3 ), 3, 0), ' ', '0')
         + REPLACE(STR(PARSENAME( @IP, 2 ), 3, 0), ' ', '0')
         + REPLACE(STR(PARSENAME( @IP, 1 ), 3, 0), ' ', '0')
END
GO

DECLARE @search varchar(50) = '192.168.2.50';
WITH DATA AS (
    SELECT * FROM ( values 
        (1, 'Lucas', '192.130.1.1', '192.130.1.254'),
        (2, 'Maria', '192.168.2.1', '192.168.2.254'),
        (3, 'Julia', '192.150.3.1', '192.150.3.254')
    ) AS tbl (ID,Name,IPLow,IPHigh)
)
SELECT *
FROM DATA
WHERE dbo.IP_Comparable(@search) BETWEEN dbo.IP_Comparable(IPLow) AND dbo.IP_Comparable(IPHigh)

This will avoid the issue you're having with integer overflows.

Related