【问题标题】:Convert IP to 32-bit Binary in TSQL [duplicate]在TSQL中将IP转换为32位二进制[重复]
【发布时间】:2013-12-24 03:20:56
【问题描述】:

我有以下存储过程,可在 TSQL 中将 IP 转换为 32 位二进制文​​件。它可以工作,但速度极慢(在 40 分钟内只转换了 8,400 个)——可能是因为它使用了游标。有没有人有提高性能的建议和/或不同的方法?

这是一个示例:1.1.79.129 被转换为 00000001.00000001.01001111.10000001

谢谢

CREATE PROCEDURE [dbo].[sp_UpdateTableConvertIPToBinary]
AS

SET NOCOUNT ON

declare @IP nvarchar(255)

declare IPList cursor for
/*  
    Get IP address from CIDR Block where Binary has not been assigned
*/
select left(IP,charindex('/',IP)-1) as Block from MyDB.dbo.MyTable
WHERE IP IS NOT NULL AND [Binary] IS NULL 
ORDER BY Block
OPEN IPList
FETCH NEXT FROM IPList 
INTO @IP
WHILE @@FETCH_STATUS = 0
BEGIN

    begin                   
        declare @Octet as varchar(15)
        declare @Div as integer

        declare @Output as varchar(100)
        declare @n as integer

        declare @OriginalIP varchar(15)

        select @OriginalIP = @IP
        select @Div = '128'
        select @Output = ''

        select @n = 0

        WHILE @n < 4
            begin
                /*
                    Set @Octet = Class to the left of the first '.' in @IP
                    If no '.' in @IP, set @Octet = entire @IP (this will happen for the last Class)
                */      
                IF CHARINDEX('.',@IP) > 0 
                    begin
                        select @Octet = left(@IP,charindex('.',@IP)-1)                      
                    end
                else
                    begin
                        select @Octet = @IP
                    end

                /*                  
                    If @Octet >= 128, append 1 to @Output and subtract 128 from @Octet 
                    If not, append 0 to @Output 
                */
                if cast(@Octet as int) >= @Div
                    begin                       
                        select @Output = @Output + '1'                      
                        select @Octet = cast(@Octet as int) - @Div
                    end
                    else
                        begin
                            select @Output = @Output + '0'
                        end

                /*                  
                    If @Octet >= 64, append 1 to @Output and subtract 64 from @Octet 
                    If not, append 0 to @Output 
                */
                if cast(@Octet as int) >= (@Div/2)
                    begin
                        select @Output = @Output + '1'
                        select @Octet = cast(@Octet as int) - (@Div/2)
                    end
                    else
                        begin
                            select @Output = @Output + '0'
                        end

                /*                  
                    If @Octet >= 32, append 1 to @Output and subtract 32 from @Octet 
                    If not, append 0 to @Output 
                */
                if cast(@Octet as int) >= (@Div/4)
                    begin
                        select @Output = @Output + '1'
                        select @Octet = cast(@Octet as int) - (@Div/4)
                    end
                    else
                        begin
                            select @Output = @Output + '0'
                        end

                /*                  
                    If @Octet >= 16, append 1 to @Output and subtract 16 from @Octet 
                    If not, append 0 to @Output 
                */
                if cast(@Octet as int) >= (@Div/8)
                    begin
                        select @Output = @Output + '1'
                        select @Octet = cast(@Octet as int) - (@Div/8)
                    end
                    else
                        begin
                            select @Output = @Output + '0'
                        end

                /*                  
                    If @Octet >= 8, append 1 to @Output and subtract 8 from @Octet 
                    If not, append 0 to @Output 
                */
                if cast(@Octet as int) >= (@Div/16)
                    begin
                        select @Output = @Output + '1'
                        select @Octet = cast(@Octet as int) - (@Div/16)
                    end
                    else
                        begin
                            select @Output = @Output + '0'
                        end

                /*                  
                    If @Octet >= 4, append 1 to @Output and subtract 4 from @Octet 
                    If not, append 0 to @Output 
                */
                if cast(@Octet as int) >= (@Div/32)
                    begin
                        select @Output = @Output + '1'
                        select @Octet = cast(@Octet as int) - (@Div/32)
                    end
                    else
                        begin
                            select @Output = @Output + '0'
                        end 

                /*                  
                    If @Octet >= 2, append 1 to @Output and subtract 2 from @Octet 
                    If not, append 0 to @Output 
                */
                if cast(@Octet as int) >= (@Div/64)
                    begin
                        select @Output = @Output + '1'
                        select @Octet = cast(@Octet as int) - (@Div/64)
                    end
                    else
                        begin
                            select @Output = @Output + '0'
                        end

                /*                  
                    If @Octet >= 1, append 1 to @Output and subtract 1 from @Octet 
                    If not, append 0 to @Output 
                */
                if cast(@Octet as int) >= (@Div/128)
                    begin
                        select @Output = @Output + '1'
                        select @Octet = cast(@Octet as int) - (@Div/128)
                    end
                    else
                        begin
                            select @Output = @Output + '0'
                        end                     

                /* 
                    if @n < 3, append . to @Output
                */  
                if @n < 3 
                    begin
                        select @Output = @Output + '.'
                    end 

            /*
                Remove the Octet just converted to Binary from @IP and increment the counter
            */
            select @IP = right(@IP,len(@IP) - charindex('.',@IP))           
            select @n = @n+1            
        end 
            /*
                Update table, set Binary = @Output 
            */
            UPDATE MyDB.dbo.MyTable Set Binary = @Output WHERE left(IP,charindex('/',IP)-1) = @OriginalIP                                   
        end

  FETCH NEXT FROM IPList 
    INTO @IP
END
CLOSE IPList
DEALLOCATE IPList   

【问题讨论】:

  • 这是 IPV4 还是 IPV6?
  • 另外,如果您能向我们提供示例输入行和预期输出,那将非常有帮助。不需要太多,几个就可以了。
  • 为什么不是二进制(4)? stackoverflow.com/questions/1385552/…

标签: sql-server tsql cidr


【解决方案1】:

对于那些正在寻找一次性 IP4 到 bigint 转换而不是现有存储过程的性能问题的人。下面是进行转换的示例内联代码

SELECT CONVERT(bigint, LEFT([IP], CHARINDEX('.', [IP]) - 1)) + 
   CONVERT(bigint, SUBSTRING([IP],
                             CHARINDEX('.', [IP]) + 1,
                             CHARINDEX('.', [IP], CHARINDEX('.', [IP]) + 1)
                                - CHARINDEX('.', [IP]) - 1)) * 256 +
   CONVERT(bigint, SUBSTRING([IP],
                             CHARINDEX('.', [IP], CHARINDEX('.', [IP]) + 1) + 1,
                             CHARINDEX('.', [IP], CHARINDEX('.', [IP], CHARINDEX('.', [IP]) + 1) + 1)
                                - CHARINDEX('.', [IP], CHARINDEX('.', [IP]) + 1) - 1)) * 65536 +
   CONVERT(bigint, RIGHT([IP],
                   LEN([IP]) - 1
                        - CHARINDEX('.', [IP], CHARINDEX('.', [IP], CHARINDEX('.', [IP]) + 1) + 1) + 1)) * 16777216
   AS Value
FROM IPAddresses
-- additional check if IPv6 addresses are in the table
WHERE CHARINDEX(':', [IP]) = 0

【讨论】:

    【解决方案2】:

    嗯,它看起来像 IPV4,所以我将继续这个假设。您还转换为一些非常长的二进制文本表示,我建议您只使用BINARY(4),我的回答会假设。如果需要,您可以将其转换为 131 个字符的文本表示形式,但我不知道您为什么要这样做。

    几年前我在这里发布了一个类似的一般问题的答案:https://stackoverflow.com/a/1385701/109122

    那基本上说明了这个功能:

    CREATE FUNCTION dbo.fnBinaryIPv4(@ip AS VARCHAR(15)) RETURNS BINARY(4)
    AS
    BEGIN
        DECLARE @bin AS BINARY(4)
    
        SELECT @bin = CAST( CAST( PARSENAME( @ip, 4 ) AS INTEGER) AS BINARY(1))
                    + CAST( CAST( PARSENAME( @ip, 3 ) AS INTEGER) AS BINARY(1))
                    + CAST( CAST( PARSENAME( @ip, 2 ) AS INTEGER) AS BINARY(1))
                    + CAST( CAST( PARSENAME( @ip, 1 ) AS INTEGER) AS BINARY(1))
    
        RETURN @bin
    END
    go
    

    但今天,我建议将其设为内联表值函数以提高性能。

    你可以这样做:

    CREATE FUNCTION dbo.itvfBinaryIPv4(@ip AS VARCHAR(15)) RETURNS TABLE
    AS RETURN (
        SELECT CAST(
                   CAST( CAST( PARSENAME( @ip, 4 ) AS INTEGER) AS BINARY(1))
                +  CAST( CAST( PARSENAME( @ip, 3 ) AS INTEGER) AS BINARY(1))
                +  CAST( CAST( PARSENAME( @ip, 2 ) AS INTEGER) AS BINARY(1))
                +  CAST( CAST( PARSENAME( @ip, 1 ) AS INTEGER) AS BINARY(1))
                    AS BINARY(4)) As bin
            )
    go
    

    这就是您如何使用它来执行转换和更新而无需光标:

    ;
    WITH cte As 
    (
        SELECT      *
        FROM        MyDB.dbo.MyTable
        OUTER APPLY dbo.itvfBinaryIPv4(left(IP,charindex('/',IP)-1)) 
    )
    UPDATE  cte
    Set     Binary = bin
    WHERE   IP IS NOT NULL 
      AND   [Binary] IS NULL 
    

    这应该很快。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-09
      • 2015-05-18
      • 2013-12-26
      • 2017-08-18
      • 1970-01-01
      • 2015-05-09
      • 1970-01-01
      • 2014-05-20
      相关资源
      最近更新 更多