1

在 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

4 回答 4

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
于 2013-07-29T13:34:54.453 回答
4

我编写了以下函数,它适用于少量行。对于较大的表,IP 应该以二进制形式存储。

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
于 2013-07-31T13:42:23.913 回答
1

就像你用任何其他语言做的一样——用一堆拼写出来的逻辑:

  1. 将字符串转换为 4 个整数
  2. 确定位掩码(网络掩码)
  3. and应用(按位)掩码后,从左到右一次比较一个数字

SQL Server 的唯一特别之处是您希望避免使用函数(内联表值函数除外)。您还需要考虑到 SQL Server 在处理字符串时速度很慢。

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

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

于 2013-07-29T11:42:31.553 回答
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;
于 2015-07-13T21:28:57.707 回答