sql-servert-sqlsql-server-2005typesip-address

Datatype for storing ip address in SQL Server


What datatype should I choose for storing an IP Address in a SQL Server?

By selecting the right datatype would it be easy enough to filter by IP address then?


Solution

  • The technically correct way to store IPv4 is binary(4), since that is what it actually is.
    (no, not even an int32/int(4), the numeric textual form that we all know and love (255.255.255.255) being just the display conversion of its binary content).

    If you do it this way, you will want functions to convert to and from the textual-display format.

    Here's how to convert the textual display form to binary:

    CREATE FUNCTION dbo.fnBinaryIPv4 (@ip AS VARCHAR(15))
    RETURNS BINARY(4)
    AS
    BEGIN
        RETURN 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))
    END
    GO
    

    And here's how to convert the binary back to the textual display form:

    CREATE FUNCTION dbo.fnDisplayIPv4 (@ip AS BINARY(4))
    RETURNS VARCHAR(15)
    AS
    BEGIN
        RETURN  CAST( CAST( SUBSTRING( @ip, 1, 1) AS INTEGER) AS VARCHAR(3) )
        + '.' + CAST( CAST( SUBSTRING( @ip, 2, 1) AS INTEGER) AS VARCHAR(3) )
        + '.' + CAST( CAST( SUBSTRING( @ip, 3, 1) AS INTEGER) AS VARCHAR(3) )
        + '.' + CAST( CAST( SUBSTRING( @ip, 4, 1) AS INTEGER) AS VARCHAR(3) )
    END
    GO
    

    Here's a demo of how to use them:

    SELECT dbo.fnBinaryIPv4( '186.190.250.206' )
    --should return 0xBABEFACE
    GO
    SELECT dbo.fnDisplayIPv4( 0xBABEFACE )
    -- should return '186.190.250.206'
    GO
    

    Finally, when doing look-ups and compares,
    always use the binary form if you want to be able to leverage your indexes
    and the smaller of the 2 data types (binary(4) is 4 bytes, varchar(15) can be 16 bytes).


    UPDATE:

    One way to address the inherent performance problems of scalar UDFs in SQL Server,
    but still retain the code-reuse of a function,
    is to use an iTVF (inline table-valued function) instead.

    Here's how the first function above (string to binary) can be re-written as an iTVF:

    CREATE FUNCTION dbo.itvfBinaryIPv4 (@ip AS VARCHAR(15))
    RETURNS TABLE
    AS
        RETURN (
            SELECT bin = 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)
                )
            )
    GO
    

    Here it is in the example:

    SELECT bin
    FROM dbo.itvfBinaryIPv4( '186.190.250.206' )
    --should return 0xBABEFACE
    

    And here's how you would use it in an INSERT and using OUTER APPLY on another table's data:

    CREATE TABLE #myIpTable (
         StringIP varchar(15)
        ,BinaryIP binary(4)
        )
    ;
    INSERT INTO #myIpTable (
         StringIP
        ,BinaryIP
        )
    SELECT x.StringIP
        ,i.bin
    FROM (VALUES
         ('192.65.68.201')
        ,('222.237.250.206')
    ) x (StringIP)
    OUTER APPLY dbo.itvfBinaryIPv4(x.StringIP) i
    ;
    SELECT *
    FROM #myIpTable
    /* should return:
    
    StringIP        BinaryIP
    192.65.68.201   0xC04144C9
    222.237.250.206 0xDEEDFACE
    */