从“子网”列创建一个具有IP地址的列

时间:2018-07-25 07:28:47

标签: sql subnet

我在SQL数据库中有两列:SubnetIDSubnetName

示例:

 SubnetID  SubnetName

 1         1.2.3.0/24
 2         1.2.4.0/14
 3         1.2.5.4/30
 ...
 ...

使用SQL代码,我需要为每个现有行(subnetID,SubnetName)添加另一列“ IP地址”。

输出:

SubnetID  SubnetName   IP Address
 1         1.2.3.0/24   1.2.3.0
 1         1.2.3.0/24   1.2.3.1
 1         1.2.3.0/24   1.2.3.2
 ...       ...          ... and so on (till .24)
 1         1.2.3.0/24   1.2.3.24
 2         1.2.4.0/14   1.2.4.0  
 2         1.2.4.0/14   1.2.4.1 
 2         1.2.4.0/14   1.2.4.2
 ...       ...          ... and so on (till .14)
 2         1.2.4.0/14   1.2.4.14  
 3         1.2.5.4/30   1.2.5.4
 3         1.2.5.4/30   1.2.5.5
 3         1.2.5.4/30   1.2.5.6
 ...       ...          ... and so on (till .30)
 3         1.2.5.4/30   1.2.5.30
 ... etc.

因此,基本上前两列需要保持原样,但其他列将具有覆盖(.x / x)范围的单独地址。

非常感谢您的任何建议。

3 个答案:

答案 0 :(得分:0)

如果我正确理解了您的问题,最好在表中添加另一个列,而不是在表中添加新列:

SubnetID  IPAddress
-------------------    
   1       1.2.3.0
   1       1.2.3.1
   1       1.2.3.2
  ...        ...
   2       1.2.4.0
  ...        ...
   3       1.2.5.4

您的原始表格保持不变:

SubnetID  SubnetName
---------------------
   1      1.2.3.0/24
   2      1.2.4.0/14
   3      1.2.5.4/30
  ...        ...

在我看来,这比您想做的要好,因为现在我们不复制所有的SubnetName值,并且现在可以避免冗余和所谓的update anomaly

例如在您提出的示例中,如果要将ID SubnetName的{​​{1}}从2更改为1.2.4.0/14会发生什么?您将不得不更改每一行-但是使用这种方式,只需更改一次即可。

答案 1 :(得分:0)

尝试此解决方案,

with cte1 as (
    select *, RNUM = 
    row_number() over (partition by SubnetID order by SubnetID,SubnetName)-1 
     from demo_25_7_2018
     )
     select *,IPADDRESS=SUBSTRING(subnetname,1,charindex('/',subnetname,1)-1)+'.'+convert(varchar,rnum)
      from cte1

Sqlfiddle演示: http://sqlfiddle.com/#!18/0057d/1

让我知道这是否可以正常工作。

答案 2 :(得分:0)

在您的问题中,我建议您在业务逻辑层上这样做。

这是 SQL服务器版本解决方案。

您可以使用 CTE递归

有一些主要步骤

  1. 使用STRING_SPLIT函数编写第一个子查询,以拆分/得到IPNumber
  2. 使用STRING_SPLIT函数编写第二个子查询,以拆分.得到IPMaxNumber并使用 CTE递归
  3. 在主查询中使用REPLACE替换IP地址。

查询

;WITH CTE AS (
    SELECT t1.SubnetID,
           t1.SubnetName,
           MAX(CASE WHEN rn = 1 THEN v.value END) startIP, 
           MAX(CASE WHEN rn = 2 THEN v.value END) MaxLevel
    FROM T t1
    CROSS APPLY (SELECT value,row_number() over (order by (select null)) rn FROM STRING_SPLIT(t1.SubnetName,'/')) v
    GROUP BY  t1.SubnetName,
              t1.SubnetID
),CTE1 AS(
   SELECT 
          t1.SubnetID,
          t1.SubnetName,
          t1.startIP,
          CAST(t1.MaxLevel AS INT) MaxLevel,
          CAST(MAX(CASE WHEN rn = 4 THEN v.value END)  AS INT) StartLevel,
          CAST(MAX(CASE WHEN rn = 4 THEN v.value END) AS INT) IPSub
   FROM CTE t1
   CROSS APPLY (SELECT value,row_number() over (order by (select null)) rn FROM STRING_SPLIT(t1.startIP,'.')) v
   GROUP BY  
          t1.SubnetName,
          t1.startIP,
          t1.MaxLevel,
          t1.SubnetID
   UNION ALL 
   SELECT 
          SubnetID,
          SubnetName,
          startIP,
          MaxLevel,
          (StartLevel + 1) StartLevel,
          IPSub
   FROM CTE1
   WHERE StartLevel < MaxLevel
)

SELECT 
  SubnetID,
  SubnetName,
  REPLACE(startIP,CONCAT('.',IPSub),CONCAT('.',StartLevel))
FROM CTE1
order by SubnetID

SQLfiddle

编辑

如果您的SQL Server版本不支持STRING_SPLIT函数。您可以编写自己的splitstring函数。

这是T-SQL split string中的自定义splitstring函数

CREATE FUNCTION dbo.splitstring (@inputStr VARCHAR(MAX),@spitChar varchar(1))
RETURNS
 @returnList TABLE ([Value] [nvarchar] (500))
AS
BEGIN

 DECLARE @name NVARCHAR(255)
 DECLARE @pos INT

 WHILE CHARINDEX(@spitChar, @inputStr) > 0
 BEGIN
  SELECT @pos  = CHARINDEX(@spitChar, @inputStr)  
  SELECT @name = SUBSTRING(@inputStr, 1, @pos-1)

  INSERT INTO @returnList 
  SELECT @name

  SELECT @inputStr = SUBSTRING(@inputStr, @pos+1, LEN(@inputStr)-@pos)
 END

 INSERT INTO @returnList
 SELECT @inputStr

 RETURN
END

然后您可以像sqlfiddle

一样使用