Convert IP to 32-bit Binary in TSQL [duplicate]

帅比萌擦擦* 提交于 2019-12-04 20:45:52
RBarryYoung

Well it looks like IPV4, so I am going to go on that assumption. You are also converting to some really long binary text representation, I recommend that you just use BINARY(4) instead, and my answer will assume that. You can convert this your 131-character text representation if you want, but I don't know why you would want to.

I posted an answer to a similar general question some years ago here: https://stackoverflow.com/a/1385701/109122

That basically showed this function:

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

Today however, I would recommend making it an inline Table-Value Function instead for performance.

This is how you could do that:

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

And this is how you could use that to perform your conversion and update without a cursor:

;
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 

This should be very fast.

A function can be used in an UPDATE so you don't need to use a cursor. Conversion to a binary string, if that's really what you need, is left as an exercise.

How about this function to convert an IPV4 string to a BIGINT:

create function [dbo].[IntegerIPV4Address]( @IPV4Address VarChar(16) )
  returns BigInt
  with SchemaBinding -- Deterministic function.
  begin
  -- NB: ParseName   is non-deterministic.
  declare @Dot1 as Int = CharIndex( '.', @IPV4Address );
  declare @Dot2 as Int = CharIndex( '.', @IPV4Address, @Dot1 + 1 );
  declare @Dot3 as Int = CharIndex( '.', @IPV4Address, @Dot2 + 1 );
  return Cast( Substring( @IPV4Address, 0, @Dot1 ) as BigInt ) * 0x1000000 +
    Cast( Substring( @IPV4Address, @Dot1 + 1, @Dot2 - @Dot1 - 1 ) as BigInt ) * 0x10000 +  
    Cast( Substring( @IPV4Address, @Dot2 + 1, @Dot3 - @Dot2 - 1 ) as BigInt ) * 0x100 +
    Cast( Substring( @IPV4Address, @Dot3 + 1, Len( @IPV4Address ) * 1 ) as BigInt );
  end

It returns a BIGINT to avoid fiddling about with negative values.

Going from a BIGINT to a VARCHAR with zero-padding I use:

create function [dbo].[NormalizedIPV4Address]( @IntegerIPV4Address as BigInt )
  returns VarChar(16)
  with SchemaBinding -- Deterministic function.
  begin
  declare @BinaryAddress as VarBinary(4) = Cast( @IntegerIPV4Address as VarBinary(4) );
  return Right( '00' + Cast( Cast( Substring( @BinaryAddress, 1, 1 ) as Int ) as VarChar(3) ), 3 ) +
    '.' + Right( '00' + Cast( Cast( Substring( @BinaryAddress, 2, 1 ) as Int ) as VarChar(3) ), 3 ) +
    '.' + Right( '00' + Cast( Cast( Substring( @BinaryAddress, 3, 1 ) as Int ) as VarChar(3) ), 3 ) +
    '.' + Right( '00' + Cast( Cast( Substring( @BinaryAddress, 4, 1 ) as Int ) as VarChar(3) ), 3 )
  end

For those who are looking for one time IP4 to bigint conversion rather then performance issues with existing stored procedures. Below is the sample inline code that makes conversion

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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!