Sql server 在TSQL中将IP转换为32位二进制

Sql server 在TSQL中将IP转换为32位二进制,sql-server,tsql,cidr,Sql Server,Tsql,Cidr,在TSQL中,我有以下将IP转换为32位二进制的存储过程。它可以工作,但速度非常慢,在40分钟内只转换了8400次,可能是因为它使用了游标。是否有人提出了改进绩效的建议和/或其他方法 下面是一个示例:1.1.79.129被转换为00000001.00000001.01001111.10000001 谢谢 CREATE PROCEDURE [dbo].[sp_UpdateTableConvertIPToBinary] AS SET NOCOUNT ON declare @IP nvarchar

在TSQL中,我有以下将IP转换为32位二进制的存储过程。它可以工作,但速度非常慢,在40分钟内只转换了8400次,可能是因为它使用了游标。是否有人提出了改进绩效的建议和/或其他方法

下面是一个示例: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,所以我将继续这个假设。您还将转换为一些非常长的二进制文本表示形式,我建议您只使用BINARY4,我的答案是这样的。如果你想的话,你可以把它转换成131个字符的文本,但我不知道你为什么要这样做

几年前,我在这里发布了一个类似一般问题的答案:

这基本上说明了这一功能:

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 

这应该非常快。

对于那些希望一次性将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

这是IPV4还是IPV6?另外,如果您能为我们提供示例输入行和预期输出,这将非常有用。不需要太多,只要两个就可以了。为什么不使用binary4呢?