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?
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
*/