寻找一种更有效的方法将 IP 地址分解为八位字节
Looking for a more efficient way to break IP addresses into octets
我有一个存储 IP 地址掩码的 table,用于验证是否允许用户根据其 IP 地址访问资源。为了验证他们的 IP 地址,我将他们的地址和掩码分成八位字节并进行比较。我有一个可行的方法,但它不太适合我,因为它似乎对字符串操作过于繁重,这在 T-SQL 中很慢。我觉得这是一种 'Brute Force' 方法,一点也不优雅。我希望有人可以提供一些改进,同时保持相同的输出。
部分蒙版示例如下:
- *.*.*.* - 这是最常见的,即“对于所有可能的 IP
地址
- 172.16.*.* - 这指定了以 172.16
开头的 IP 地址
- 172.16.0-10.* - 这指定了以 172.16 开头但第三个八位字节在 0 到 10 之间的 IP 地址
- 172.16.99.10 - 这指定了一个单独的地址。
为了解析这些,我声明了一个临时文件 table:
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)
)
我首先将数据库 table 中的记录插入我的临时文件 table:
INSERT @Rights(IPAddress, dot, tlen, tempaddr)
SELECT IPAddress, CHARINDEX('.',IPAddress), LEN(IPAddress), IPAddress
FROM IPAccessRights_Info
然后,我 运行 4 UPDATE
语句针对这个温度 table:
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 时,这将是一个完全不同的蠕虫罐...
也许我完全不在这里,但我可以想象你的原始 table 不适合使用。
如果这样定义 table 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]
当你从你的管理应用程序中插入 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.
你可以像这样查询:
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
这个Tip-of-the-day (and this one) describes how to use the T-SQL PARSENAME function
分隔 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
我有一个存储 IP 地址掩码的 table,用于验证是否允许用户根据其 IP 地址访问资源。为了验证他们的 IP 地址,我将他们的地址和掩码分成八位字节并进行比较。我有一个可行的方法,但它不太适合我,因为它似乎对字符串操作过于繁重,这在 T-SQL 中很慢。我觉得这是一种 'Brute Force' 方法,一点也不优雅。我希望有人可以提供一些改进,同时保持相同的输出。
部分蒙版示例如下:
- *.*.*.* - 这是最常见的,即“对于所有可能的 IP 地址
- 172.16.*.* - 这指定了以 172.16 开头的 IP 地址
- 172.16.0-10.* - 这指定了以 172.16 开头但第三个八位字节在 0 到 10 之间的 IP 地址
- 172.16.99.10 - 这指定了一个单独的地址。
为了解析这些,我声明了一个临时文件 table:
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)
)
我首先将数据库 table 中的记录插入我的临时文件 table:
INSERT @Rights(IPAddress, dot, tlen, tempaddr)
SELECT IPAddress, CHARINDEX('.',IPAddress), LEN(IPAddress), IPAddress
FROM IPAccessRights_Info
然后,我 运行 4 UPDATE
语句针对这个温度 table:
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 时,这将是一个完全不同的蠕虫罐...
也许我完全不在这里,但我可以想象你的原始 table 不适合使用。
如果这样定义 table 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]
当你从你的管理应用程序中插入 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.
你可以像这样查询:
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
这个Tip-of-the-day (and this one) describes how to use the T-SQL PARSENAME function 分隔 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