SQL Server将IP地址加入IP范围

时间:2013-07-29 11:01:29

标签: sql sql-server sql-server-2008

在SQL Server中,加入IPv4地址表的最简单方法是什么......

IPAddress
------------
10.70.80.34
10.70.81.60
10.70.81.205

到CIDR表示法中的IP范围表...

IPRange          Description
---------------  -----------
10.70.80.0/24    Sydney
10.70.81.0/25    Melbourne
10.70.81.128/25  Perth

IP范围表中的行数少于100行。

4 个答案:

答案 0 :(得分:4)

在这种情况下,ParseName()功能可能对您有用

DECLARE @ips table (
   ip_address varchar(15)
);

INSERT INTO @ips (ip_address)
  VALUES ('10.70.80.34')
       , ('10.70.81.60')
       , ('10.70.81.205');

SELECT ip_address
     , ParseName(ip_address, 4) As first_octet
     , ParseName(ip_address, 3) As second_octet
     , ParseName(ip_address, 2) As third_octet
     , ParseName(ip_address, 1) As fourth_octet
FROM   @ips

结果

ip_address      first_octet   second_octet   third_octet  fourth_octet
--------------- ------------- -------------- ------------ -------------
10.70.80.34     10            70             80           34
10.70.81.60     10            70             81           60
10.70.81.205    10            70             81           205

答案 1 :(得分:4)

我写了以下函数,对于少量行是可以的。对于较大的表,IP应存储in binary form

CREATE FUNCTION IPAddressInRange
(
    @IPAddress NVARCHAR(MAX),
    @IPRange NVARCHAR(MAX)
)
RETURNS BIT AS
BEGIN
    DECLARE @SlashPos INT = CHARINDEX('/', @IPRange);
    DECLARE @Network NVARCHAR(MAX) = SUBSTRING(@IPRange, 1, @SlashPos - 1);
    DECLARE @PrefixBits INT = CAST(SUBSTRING(@IPRange, @SlashPos + 1, 2) AS INT);

    DECLARE @IPAddressInt BIGINT =
        PARSENAME(@IPAddress, 4) * POWER(CAST(2 AS BIGINT), 24) +
        PARSENAME(@IPAddress, 3) * POWER(CAST(2 AS BIGINT), 16) +
        PARSENAME(@IPAddress, 2) * POWER(CAST(2 AS BIGINT), 8) +
        PARSENAME(@IPAddress, 1);

    DECLARE @NetworkInt BIGINT =
        PARSENAME(@Network, 4) * POWER(CAST(2 AS BIGINT), 24) +
        PARSENAME(@Network, 3) * POWER(CAST(2 AS BIGINT), 16) +
        PARSENAME(@Network, 2) * POWER(CAST(2 AS BIGINT), 8) +
        PARSENAME(@Network, 1);

    DECLARE @Mask BIGINT = POWER(CAST(2 AS BIGINT), 32) -
        POWER(CAST(2 AS BIGINT), 32 - @PrefixBits);

    RETURN CASE WHEN @IPAddressInt & @Mask = @NetworkInt THEN 1 ELSE 0 END;
END

使用示例:

SELECT *
FROM IPAddressTable a
JOIN IPRangeTable r
ON dbo.IPAddressInRange(a.IPAddress, r.IPRange) = 1

答案 2 :(得分:1)

就像你用其他任何语言一样 - 用一堆拼写的逻辑:

  1. 将字符串转换为4个整数
  2. 确定位掩码(网络掩码)
  3. 应用(按位and)面具后,从左到右一次比较一个数字
  4. SQL Server唯一特别之处在于您要避免使用函数(内联表值函数除外)。您还需要考虑到SQL操作系统在字符串操作方面的速度很慢。

    如果值存储为字符串,我建议写一个CLR函数。在.NET代码中,这实现起来相当简单,并且CLR标量值函数的调用开销并不大。但是,您将无法在任何一个表上使用索引。

    如果IPRange是一个普通的查找表,你可能会没事的。但是,如果要编写返回属于特定范围的所有IP地址的查询,则可能会出现问题。在这种情况下,我建议在单独的字段中存储IP地址的四个整数以及掩码的整数。这样就可以进行索引,并且连接逻辑变成一个相当简单的表达式。

答案 3 :(得分:0)

如果您使用PostgreSQL,则有一种更简单的方法:

CREATE OR REPLACE FUNCTION IPInRange(VARCHAR(16), VARCHAR(20))
RETURNS BOOLEAN AS $$
    SELECT CAST($1 AS inet) << CAST($2 AS inet);
$$ LANGUAGE SQL;