选择两个IP范围之间的记录

时间:2016-03-23 11:50:14

标签: sql sql-server

我有一张表格可存储IDNameCodeIPLowIPHigh,例如:

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

现在,如果我有一个IP地址192.168.2.50,我该如何检索匹配记录?

修改

根据戈登的回答(我收到编译错误),这就是我所拥有的:

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;

但这给了我一个错误:

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

有什么想法吗?

8 个答案:

答案 0 :(得分:7)

痛苦地。 SQL Server具有糟糕的字符串操作功能。但是,它提供了autosubmit。此方法将IP地址转换为较大的十进制值以进行比较:

parsename()

我应该注意,IP地址通常作为4字节无符号整数存储在数据库中。这使得比较更容易。 。 。虽然你需要复杂的逻辑(通常包含在函数中)来将值转换为可读格式。

答案 1 :(得分:5)

尝试这种检查范围的简单方法

DECLARE @IP NVARCHAR(30)='192.168.500.1'

SELECT  * FROM 
Branches
WHERE
CAST (PARSENAME(@IP,4) AS INT)>=CAST(PARSENAME(IPLow,4) AS INT) AND CAST(PARSENAME(@IP,3) AS INT)>=CAST(PARSENAME(IPLow,3) AS INT) AND CAST(PARSENAME(@IP,2) AS INT)>=CAST(PARSENAME(IPLow,2) AS INT) AND CAST(PARSENAME(@IP,1) AS INT)>=CAST(PARSENAME(IPLow,1) AS INT)
AND
CAST(PARSENAME( @IP,4) AS INT) <= CAST(PARSENAME(IPHigh ,4) AS INT) AND CAST(PARSENAME(@IP ,3) AS INT) <=CAST(PARSENAME(IPHigh ,3) AS INT) AND CAST(PARSENAME(@IP ,2) AS INT) <=CAST(PARSENAME(IPHigh ,2) AS INT) AND CAST(PARSENAME(@IP ,1) AS INT)<=CAST(PARSENAME(IPHigh ,1) AS INT)

AS Per @Ed Haper评论演员需要。

答案 2 :(得分:3)

使用此功能,您可以将任何IP地址转换为每个部分有3位数字的表单。有了这个,您可以进行正常的字母数字比较。如果你想要,你也可以返回BIGINT ......

CREATE FUNCTION dbo.IPWidth3(@IP VARCHAR(100))
RETURNS VARCHAR(15)
BEGIN
DECLARE @RetVal VARCHAR(15);
WITH Splitted AS
(
    SELECT CAST('<x>' + REPLACE(@IP,'.','</x><x>') + '</x>' AS XML) AS IPSplitted 
)
SELECT @RetVal = STUFF(
        (
        SELECT '.' + REPLACE(STR(Part.value('.','int'),3),' ','0')
        FROM Splitted.IPSplitted.nodes('/x') AS One(Part)
        FOR XML PATH('')
        ),1,1,'') 
FROM Splitted;

RETURN @RetVal;
END
GO

DECLARE @IP VARCHAR(100)='192.43.2.50';
SELECT dbo.IPWidth3(@IP);

结果

192.043.002.050

在这里反映Ed Harper的评论,返回DECIMAL(12,0)

的相同函数
CREATE FUNCTION dbo.IP_as_Number(@IP VARCHAR(100))
RETURNS DECIMAL(12,0)
BEGIN
DECLARE @RetVal DECIMAL(12,0);
WITH Splitted AS
(
    SELECT CAST('<x>' + REPLACE(@IP,'.','</x><x>') + '</x>' AS XML) AS IPSplitted 
)
SELECT @RetVal = 
        CAST((
        SELECT REPLACE(STR(Part.value('.','int'),3),' ','0')
        FROM Splitted.IPSplitted.nodes('/x') AS One(Part)
        FOR XML PATH('')
        ) AS DECIMAL(12,0))
FROM Splitted;

RETURN @RetVal;
END
GO

DECLARE @IP VARCHAR(100)='192.43.2.50';
SELECT dbo.IP_as_Number(@IP);

答案 3 :(得分:1)

使用以下方法获取4列中的ipLow / IPHigh。您可以使用这些列来比较Ips。

DECLARE@ip VARCHAR(50)='192.168.0.81' 
SELECT (SUBSTRING((@ip), 0,
patindex('%.%',
(@ip))))

,
substring((REPLACE(@ip, (SUBSTRING((@ip), 0,
patindex('%.%',
(@ip)) + 1)),
'')),
0,
patindex('%.%',
((REPLACE(@ip, (SUBSTRING((@ip), 0,
patindex('%.%',
(@ip)) + 1)),
''))))),
SUBSTRING((SUBSTRING(@ip, LEN((SUBSTRING((@ip), 0,
patindex('%.%',
(@ip))))) + 2 + LEN(substring((REPLACE(@ip, (SUBSTRING((@ip), 0,
patindex('%.%',
(@ip)) + 1)),
'')),
0,
patindex('%.%',
((REPLACE(@ip, (SUBSTRING((@ip), 0,
patindex('%.%',
(@ip)) + 1)),
'')))))) + 1,
LEN(@IP) - 1 - LEN(reverse(SUBSTRING(reverse(@ip), 0,
patindex('%.%',
reverse(@ip))))))), 0,
PATINDEX('%.%',
(SUBSTRING(@ip, LEN((SUBSTRING((@ip), 0,
patindex('%.%',
(@ip))))) + 2 + LEN(substring((REPLACE(@ip, (SUBSTRING((@ip), 0,
patindex('%.%',
(@ip)) + 1)),
'')),
0,
patindex('%.%',
((REPLACE(@ip, (SUBSTRING((@ip), 0,
patindex('%.%',
(@ip)) + 1)),
'')))))) + 1,
LEN(@IP) - 1 - LEN(reverse(SUBSTRING(reverse(@ip), 0,
patindex('%.%',
reverse(@ip))))))

))),
reverse(SUBSTRING(reverse(@ip), 0,
patindex('%.%',
reverse(@ip))))

答案 4 :(得分:1)

考虑这个例子,将地址转换为数字。

CREATE FUNCTION dbo.IPAddressAsNumber (@IPAddress AS varchar(15))
RETURNS bigint
BEGIN
RETURN
 CONVERT (bigint,
  CONVERT(varbinary(1), CONVERT(int, PARSENAME(@IPAddress, 4))) +
  CONVERT(varbinary(1), CONVERT(int, PARSENAME(@IPAddress, 3))) +
  CONVERT(varbinary(1), CONVERT(int, PARSENAME(@IPAddress, 2))) +
  CONVERT(varbinary(1), CONVERT(int, PARSENAME(@IPAddress, 1))) )
END

并且你可以使用像BETWEEN这样的标准运算符来查找表中范围内的行

DECLARE @t table (ID int, Name varchar(50), Code int, IPLow varchar(15), IPHigh varchar(15))
INSERT INTO @t VALUES 
 (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')

SELECT * FROM @t
WHERE dbo.IPAddressAsNumber('192.168.2.50')
 BETWEEN dbo.IPAddressAsNumber(IPLow) AND dbo.IPAddressAsNumber(IPHigh)

该方案实质上使用PARSENAME来隔离地址的每个部分,将每个部分转换为SQL二进制字符串,将字符串连接在一起以获得表示地址的单个SQL二进制字符串,并将结果显示为bigint。

在十六进制值的文本表示中,将其视为粉碎4个部分192(0xC0)+ 168(0xA8)+ 2(0x02)+ 50(0x32)到0xC0A80232。当您将组合字符串转换为其二进制数字(0和1)时,您最终会得到一些可以被认为是地址路由和子网掩码表中的网络堆栈使用的二进制形式的地址。当你将它转换为无符号整数形式的数字(或者在这种情况下是bigint)时,你会得到3232236082。

有趣的是,这个方案为您提供了一个&#34;数字&#34;可以用很多方式代替原始地址。例如,您可以ping数字2130706433而不是地址127.0.0.1 - Windows中的名称解析器将转换它,类似于DNS用于查找主机名地址的方式。

为了完整起见,这里有另一个函数可用于将数字形式转换回标准字符串形式

CREATE FUNCTION dbo.IPAddressFromNumber (@IPNumber AS bigint)
RETURNS varchar(15)
BEGIN
RETURN
 CONVERT (varchar(15),
  CONVERT(varchar(3), CONVERT(int, SUBSTRING(CONVERT(varbinary(4), @IPNumber), 1,1))) + '.' +
  CONVERT(varchar(3), CONVERT(int, SUBSTRING(CONVERT(varbinary(4), @IPNumber), 2,1))) + '.' +
  CONVERT(varchar(3), CONVERT(int, SUBSTRING(CONVERT(varbinary(4), @IPNumber), 3,1))) + '.' +
  CONVERT(varchar(3), CONVERT(int, SUBSTRING(CONVERT(varbinary(4), @IPNumber), 4,1))) )
END

答案 5 :(得分:0)

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

在此,表“ a”包含IP地址列表,表“ b”包含IP范围。

我们可以直接比较字符串,而不是将IP地址转换为数字,而是逐字节比较。

这对我有用(PostgreSQL)。

答案 6 :(得分:0)

我一直在按照戈登的答案思考,然后意识到您实际上并不需要弄乱数字。如果您将地址的每个部分都用零填充,则可以进行字符串比较:

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')

当然,您可以将其放在UDF中以简化操作,尽管要注意大型查询对性能的影响。

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)

这将避免整数溢出问题。

答案 7 :(得分:-4)

取决于您正在寻找高或低的记录。

select * from table where IPlow like '192.168.2.50' or IPHigh like '192.168.2.50'