Sql 寻找一种更有效的方法将IP地址分成八位字节

Sql 寻找一种更有效的方法将IP地址分成八位字节,sql,tsql,Sql,Tsql,我有一个存储IP地址掩码的表,用于验证是否允许用户基于其IP地址访问资源。为了验证他们的IP地址,我将他们的地址和掩码分成八位字节并进行比较。我有一个可行的方法,但它并不适合我,因为它似乎对字符串操作过于繁重,而字符串操作在t-SQL中很慢。我觉得这是一种“蛮力”方法,一点也不优雅。我希望有人能提供一些改进,同时保持相同的输出 一些示例遮罩如下所示: *这是最常见的,适用于所有可能的IP 地址 172.16.*.-指定以172.16开头的IP地址 172.16.0-10.*-指定以172.16开

我有一个存储IP地址掩码的表,用于验证是否允许用户基于其IP地址访问资源。为了验证他们的IP地址,我将他们的地址和掩码分成八位字节并进行比较。我有一个可行的方法,但它并不适合我,因为它似乎对字符串操作过于繁重,而字符串操作在t-SQL中很慢。我觉得这是一种“蛮力”方法,一点也不优雅。我希望有人能提供一些改进,同时保持相同的输出

一些示例遮罩如下所示:

*这是最常见的,适用于所有可能的IP 地址 172.16.*.-指定以172.16开头的IP地址 172.16.0-10.*-指定以172.16开头的IP地址,但第三个八位组在0和10之间 172.16.99.10-指定单个地址。 为了解析这些,我声明了一个临时表:

DECLARE @Rights TABLE(
IPAddr char(15)
, octet1 char(7)
, octet1max char(3)
, octet2 char(7)
, octet2max char(3)
, octet3 char(7)
, octet3max char(3)
, octet4 char(7)
, octet4max char(3)
, dot int
, tlen int
, tempaddr char(15)
)
INSERT @Rights(IPAddress, dot, tlen, tempaddr)
SELECT IPAddress, CHARINDEX('.',IPAddress), LEN(IPAddress), IPAddress
FROM IPAccessRights_Info
UPDATE @Rights
SET octet1 = SUBSTRING(tempaddr, 0, dot)
    , tempaddr = SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot)
    , dot = CHARINDEX('.', SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot))
    , tlen = LEN(SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot))
UPDATE @Rights
SET octet2 = SUBSTRING(tempaddr, 0, dot)
    , tempaddr = SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot)
    , dot = CHARINDEX('.', SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot))
    , tlen = LEN(SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot))
UPDATE @Rights
SET octet3 = SUBSTRING(tempaddr, 0, dot)
    , octet4 = SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot)
    , dot = NULL
    , tlen = LEN(IPAddr)
    , tempaddr = NULL

-- Parse out any ranges
UPDATE @Rights
    SET octet1 = CASE WHEN CHARINDEX('-', octet1) > 0 THEN SUBSTRING(octet1, 0, CHARINDEX('-', octet1)) ELSE octet1 END
        , octet1max = CASE WHEN CHARINDEX('-', octet1) > 0 THEN SUBSTRING(octet1, CHARINDEX('-', octet1)+1, LEN(octet1)) ELSE NULL END
        , octet2 = CASE WHEN CHARINDEX('-', octet2) > 0 THEN SUBSTRING(octet2, 0, CHARINDEX('-', octet2)) ELSE octet2 END
        , octet2max = CASE WHEN CHARINDEX('-', octet2) > 0 THEN SUBSTRING(octet2, CHARINDEX('-', octet2)+1, LEN(octet2)) ELSE NULL END
        , octet3 = CASE WHEN CHARINDEX('-', octet3) > 0 THEN SUBSTRING(octet3, 0, CHARINDEX('-', octet3)) ELSE octet3 END
        , octet3max = CASE WHEN CHARINDEX('-', octet3) > 0 THEN SUBSTRING(octet3, CHARINDEX('-', octet3)+1, LEN(octet3)) ELSE NULL END
        , octet4 = CASE WHEN CHARINDEX('-', octet4) > 0 THEN SUBSTRING(octet4, 0, CHARINDEX('-', octet4)) ELSE octet4 END
        , octet4max = CASE WHEN CHARINDEX('-', octet4) > 0 THEN SUBSTRING(octet4, CHARINDEX('-', octet4)+1, LEN(octet4)) ELSE NULL END
我首先将数据库表中的记录插入临时表:

DECLARE @Rights TABLE(
IPAddr char(15)
, octet1 char(7)
, octet1max char(3)
, octet2 char(7)
, octet2max char(3)
, octet3 char(7)
, octet3max char(3)
, octet4 char(7)
, octet4max char(3)
, dot int
, tlen int
, tempaddr char(15)
)
INSERT @Rights(IPAddress, dot, tlen, tempaddr)
SELECT IPAddress, CHARINDEX('.',IPAddress), LEN(IPAddress), IPAddress
FROM IPAccessRights_Info
UPDATE @Rights
SET octet1 = SUBSTRING(tempaddr, 0, dot)
    , tempaddr = SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot)
    , dot = CHARINDEX('.', SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot))
    , tlen = LEN(SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot))
UPDATE @Rights
SET octet2 = SUBSTRING(tempaddr, 0, dot)
    , tempaddr = SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot)
    , dot = CHARINDEX('.', SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot))
    , tlen = LEN(SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot))
UPDATE @Rights
SET octet3 = SUBSTRING(tempaddr, 0, dot)
    , octet4 = SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot)
    , dot = NULL
    , tlen = LEN(IPAddr)
    , tempaddr = NULL

-- Parse out any ranges
UPDATE @Rights
    SET octet1 = CASE WHEN CHARINDEX('-', octet1) > 0 THEN SUBSTRING(octet1, 0, CHARINDEX('-', octet1)) ELSE octet1 END
        , octet1max = CASE WHEN CHARINDEX('-', octet1) > 0 THEN SUBSTRING(octet1, CHARINDEX('-', octet1)+1, LEN(octet1)) ELSE NULL END
        , octet2 = CASE WHEN CHARINDEX('-', octet2) > 0 THEN SUBSTRING(octet2, 0, CHARINDEX('-', octet2)) ELSE octet2 END
        , octet2max = CASE WHEN CHARINDEX('-', octet2) > 0 THEN SUBSTRING(octet2, CHARINDEX('-', octet2)+1, LEN(octet2)) ELSE NULL END
        , octet3 = CASE WHEN CHARINDEX('-', octet3) > 0 THEN SUBSTRING(octet3, 0, CHARINDEX('-', octet3)) ELSE octet3 END
        , octet3max = CASE WHEN CHARINDEX('-', octet3) > 0 THEN SUBSTRING(octet3, CHARINDEX('-', octet3)+1, LEN(octet3)) ELSE NULL END
        , octet4 = CASE WHEN CHARINDEX('-', octet4) > 0 THEN SUBSTRING(octet4, 0, CHARINDEX('-', octet4)) ELSE octet4 END
        , octet4max = CASE WHEN CHARINDEX('-', octet4) > 0 THEN SUBSTRING(octet4, CHARINDEX('-', octet4)+1, LEN(octet4)) ELSE NULL END
然后,我对这个临时表运行4条UPDATE语句:

DECLARE @Rights TABLE(
IPAddr char(15)
, octet1 char(7)
, octet1max char(3)
, octet2 char(7)
, octet2max char(3)
, octet3 char(7)
, octet3max char(3)
, octet4 char(7)
, octet4max char(3)
, dot int
, tlen int
, tempaddr char(15)
)
INSERT @Rights(IPAddress, dot, tlen, tempaddr)
SELECT IPAddress, CHARINDEX('.',IPAddress), LEN(IPAddress), IPAddress
FROM IPAccessRights_Info
UPDATE @Rights
SET octet1 = SUBSTRING(tempaddr, 0, dot)
    , tempaddr = SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot)
    , dot = CHARINDEX('.', SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot))
    , tlen = LEN(SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot))
UPDATE @Rights
SET octet2 = SUBSTRING(tempaddr, 0, dot)
    , tempaddr = SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot)
    , dot = CHARINDEX('.', SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot))
    , tlen = LEN(SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot))
UPDATE @Rights
SET octet3 = SUBSTRING(tempaddr, 0, dot)
    , octet4 = SUBSTRING(tempaddr, dot+1, LEN(tempaddr)-dot)
    , dot = NULL
    , tlen = LEN(IPAddr)
    , tempaddr = NULL

-- Parse out any ranges
UPDATE @Rights
    SET octet1 = CASE WHEN CHARINDEX('-', octet1) > 0 THEN SUBSTRING(octet1, 0, CHARINDEX('-', octet1)) ELSE octet1 END
        , octet1max = CASE WHEN CHARINDEX('-', octet1) > 0 THEN SUBSTRING(octet1, CHARINDEX('-', octet1)+1, LEN(octet1)) ELSE NULL END
        , octet2 = CASE WHEN CHARINDEX('-', octet2) > 0 THEN SUBSTRING(octet2, 0, CHARINDEX('-', octet2)) ELSE octet2 END
        , octet2max = CASE WHEN CHARINDEX('-', octet2) > 0 THEN SUBSTRING(octet2, CHARINDEX('-', octet2)+1, LEN(octet2)) ELSE NULL END
        , octet3 = CASE WHEN CHARINDEX('-', octet3) > 0 THEN SUBSTRING(octet3, 0, CHARINDEX('-', octet3)) ELSE octet3 END
        , octet3max = CASE WHEN CHARINDEX('-', octet3) > 0 THEN SUBSTRING(octet3, CHARINDEX('-', octet3)+1, LEN(octet3)) ELSE NULL END
        , octet4 = CASE WHEN CHARINDEX('-', octet4) > 0 THEN SUBSTRING(octet4, 0, CHARINDEX('-', octet4)) ELSE octet4 END
        , octet4max = CASE WHEN CHARINDEX('-', octet4) > 0 THEN SUBSTRING(octet4, CHARINDEX('-', octet4)+1, LEN(octet4)) ELSE NULL END
完成后,我可以使用octet1-octet4和octet1max-octet4max中的值来匹配我的IP地址


当然,当我们都使用IPv6时,这将是一个完全不同的蠕虫罐…

也许我完全不在这里,但我可以想象,您的原始表不适合使用

If将按如下方式定义表IPAccessRights_Info:

CREATE TABLE [dbo].[IPAccessRights_Info](
    [Id] [int] NOT NULL,
    [IpAddress] [nvarchar](15) NOT NULL,
    [Octet1Min] [int] NOT NULL,
    [Octet1Max] [int] NOT NULL,
    [Octet2Min] [int] NOT NULL,
    [Octet2Max] [int] NOT NULL,
    [Octet3Min] [int] NOT NULL,
    [Octet3Max] [int] NOT NULL,
    [Octet4Min] [int] NOT NULL,
    [Octet4Max] [int] NOT NULL,
 CONSTRAINT [PK_IPAccessRights_Info] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
INSERT INTO dbo.IPAccessRights_Info VALUES (4,'172.16.9-10.*',172,172,16,16,9,10,0,255)

-- IpAddress to check
DECLARE @IpAddress nvarchar(15) = '172.16.10.5'

DECLARE @octet1 nvarchar(3) = SUBSTRING(@IpAddress, 0, CHARINDEX('.', @IpAddress)) 
SET @IpAddress = RIGHT(@IpAddress, LEN(@IpAddress)-LEN(@octet1) - 1)
DECLARE @octet2 nvarchar(3) = SUBSTRING(@IpAddress, 0, CHARINDEX('.', @IpAddress)) 
SET @IpAddress = RIGHT(@IpAddress, LEN(@IpAddress)-LEN(@octet2) - 1)
DECLARE @octet3 nvarchar(3) = SUBSTRING(@IpAddress, 0, CHARINDEX('.', @IpAddress)) 
SET @IpAddress = RIGHT(@IpAddress, LEN(@IpAddress)-LEN(@octet3) - 1)
DECLARE @octet4 nvarchar(3) = @IpAddress

SELECT  Id
FROM    dbo.IPAccessRights_Info
WHERE   Octet1Min <= CAST(@octet1 AS int)
AND     Octet1Max >= CAST(@octet1 AS int)
AND     Octet2Min <= CAST(@octet2 AS int)
AND     Octet2Max >= CAST(@octet2 AS int)
AND     Octet3Min <= CAST(@octet3 AS int)
AND     Octet3Max >= CAST(@octet3 AS int)
AND     Octet4Min <= CAST(@octet4 AS int)
AND     Octet4Max >= CAST(@octet4 AS int)

-- Results:
    1
    2
    4
当你从你的管理应用程序中插入IpAddress权限时,我假设你有某种管理层,你要做的是把IpAddress的值分成八位字节,这项任务要简单得多

我想你只需要一个OctetMin和OctetMax列。根据您的示例翻译:

*.*.*.* => Octet1Min = 0, Octet1Max = 255, Octet2Min = 0, etc.
172.16.*.* => Octet1Min = 172, Octet1Max = 172, etc.
172.16.0-10.* => Octet3Min = 0, Octet3Max = 10, etc.
172.16.99.10 => Octet4Min = 10, Octet4Max = 10, etc.
您可以对此进行查询,如下所示:

CREATE TABLE [dbo].[IPAccessRights_Info](
    [Id] [int] NOT NULL,
    [IpAddress] [nvarchar](15) NOT NULL,
    [Octet1Min] [int] NOT NULL,
    [Octet1Max] [int] NOT NULL,
    [Octet2Min] [int] NOT NULL,
    [Octet2Max] [int] NOT NULL,
    [Octet3Min] [int] NOT NULL,
    [Octet3Max] [int] NOT NULL,
    [Octet4Min] [int] NOT NULL,
    [Octet4Max] [int] NOT NULL,
 CONSTRAINT [PK_IPAccessRights_Info] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
INSERT INTO dbo.IPAccessRights_Info VALUES (4,'172.16.9-10.*',172,172,16,16,9,10,0,255)

-- IpAddress to check
DECLARE @IpAddress nvarchar(15) = '172.16.10.5'

DECLARE @octet1 nvarchar(3) = SUBSTRING(@IpAddress, 0, CHARINDEX('.', @IpAddress)) 
SET @IpAddress = RIGHT(@IpAddress, LEN(@IpAddress)-LEN(@octet1) - 1)
DECLARE @octet2 nvarchar(3) = SUBSTRING(@IpAddress, 0, CHARINDEX('.', @IpAddress)) 
SET @IpAddress = RIGHT(@IpAddress, LEN(@IpAddress)-LEN(@octet2) - 1)
DECLARE @octet3 nvarchar(3) = SUBSTRING(@IpAddress, 0, CHARINDEX('.', @IpAddress)) 
SET @IpAddress = RIGHT(@IpAddress, LEN(@IpAddress)-LEN(@octet3) - 1)
DECLARE @octet4 nvarchar(3) = @IpAddress

SELECT  Id
FROM    dbo.IPAccessRights_Info
WHERE   Octet1Min <= CAST(@octet1 AS int)
AND     Octet1Max >= CAST(@octet1 AS int)
AND     Octet2Min <= CAST(@octet2 AS int)
AND     Octet2Max >= CAST(@octet2 AS int)
AND     Octet3Min <= CAST(@octet3 AS int)
AND     Octet3Max >= CAST(@octet3 AS int)
AND     Octet4Min <= CAST(@octet4 AS int)
AND     Octet4Max >= CAST(@octet4 AS int)

-- Results:
    1
    2
    4
这描述了如何使用 分离IP地址以及由4部分组成的数据库对象名称

DECLARE @IPAddresses TABLE ( [IPAddress] VARCHAR(20))

INSERT INTO @IPAddresses VALUES ('10.0.0.1')
INSERT INTO @IPAddresses VALUES ('255.255.255.255')
INSERT INTO @IPAddresses VALUES ('192.123.545.12')
INSERT INTO @IPAddresses VALUES ('1.2.3.4')

SELECT * FROM @IPAddresses
ORDER BY CAST(PARSENAME([IPAddress], 4) AS INT),
         CAST(PARSENAME([IPAddress], 3) AS INT),
         CAST(PARSENAME([IPAddress], 2) AS INT),
         CAST(PARSENAME([IPAddress], 1) AS INT)

IPAddress
----------------
1.2.3.4
10.0.0.1
192.123.545.12
255.255.255.255

如果您的现有代码正常工作,这可能是一个更好的问题…这看起来是SQL CLR过程和的理想任务。您可以始终避免使用字符,只使用INT,当是*时,您可以将其保留为null并进行计算。请查看T-SQL中的PARSENAME函数-不仅适用于解析由4部分组成的名称,而且适用于由句点分隔的4项中的任何一项,如IP地址:光滑!PARSENAME看起来像是我的直接冠军!我仍然需要处理任何范围,例如“10-20”,但这确实清理了代码,使其看起来更整洁。我所需要做的就是添加一条注释,帮助我记住PARSENAME是什么,以及为什么我会以如此奇怪的方式使用它,但这很容易做到!当然,这不是一个坏主意,但是底层的表结构还没有准备好进行优化。我正在承担的任务是从脆弱的应用程序中提取一些业务逻辑,并将其放入SQL存储过程。我完全不知道您正在使用的应用程序,因此选择可能有限。尽管如此,在继续向系统添加存储过程而不是将其取出之前,请阅读以下内容:没错,我保留了许多细节,以便重点关注核心问题,即解析IP地址。对我来说,我将触发器与存储过程分开。我已经学会讨厌触发器,因为调试时它们大多是看不见的。然而,存储过程有其位置。在本例中,应用程序内代码命中数据库4次,而SP将更有效地执行此操作。此外,此存储过程将在应用程序启动时调用一次,因此无论哪种方式,它对系统的影响都是最小的。