Geo IP

Re-purposing SQL Server PARSENAME For Splitting IPv4 Addresses

Geo IP
Geo IP

I stumbled across a very nice repurposing of the PARSENAME function in SQL Server recently while playing around with some GeoIP Data. In SQL Server, the PARSENAME function is used for working with fully qualified server objects. e.g. a table on a linked server (‘LinkedServerName . Databasename . Ownername . TableName’). But PARSENAME can be used to easily split up any 4 token, dot delimited string into its constituent parts.


It’s intended use, is for dealing with the string representations of server objects. e.g.

SQL

DECLARE @MyObject VARCHAR(100)
SET @MyObject = 'MyServer.MyDB.MyOwner.MyTable'

SELECT PARSENAME(@MyObject, 4) AS [ServerName],
	PARSENAME(@MyObject, 3) AS [DatabaseName],
	PARSENAME(@MyObject, 2) AS [OwnerName],
	PARSENAME(@MyObject, 1) As [TableName]

Results

ServerName   DatabaseName     OwnerName    TableName
----------- ---------------- ------------ ----------
MyServer     MyDB             MyOwner      MyTable

(1 row(s) affected)

A nice side effect of it’s functionality, is that being useful for splitting any 4 part, dot delimited string into it’s constiuent parts… e.g. an IPv4 Address; essentially saving you from performing charindex/substring gymnastics in order to split the string.

SQL

DECLARE @MyIP VARCHAR(15)
SET @MyIP = '1.12.34.45'

SELECT
	PARSENAME(@MyIP , 4) AS [octet1],
	PARSENAME(@MyIP , 3) AS [octet2],
	PARSENAME(@MyIP , 2) AS [octet3],
	PARSENAME(@MyIP , 1) As [octet4]

Results

octet1    octet2    octet3   octet4
--------- --------- ------------------
1         12        34       45

(1 row(s) affected)

If you’re using the MaxMind GeoIP Lite Database, you can very quickly access each IP range by converting the IP address to it’s equivalent Integer value and doing an indexed search for that value.

StringIP = oc4.oc3.oc2.oc1
IntIP = (oc4 * (256^3)) + (oc3 * (256^2))
       + (oc2 * (256^1)) + (oc1 * (256^0))

Here’s a sql function that’ll do just that.

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[IP_2_INT](@ip varchar(15))
RETURNS INT
WITH SCHEMABINDING
AS
BEGIN
    DECLARE @ipInt INT, @oa TINYINT, @ob TINYINT, @oc TINYINT, @od TINYINT;

    SELECT @od = CAST(PARSENAME(@ip, 4) AS TINYINT),
     @oc = CAST(PARSENAME(@ip, 3) AS TINYINT),
     @ob = CAST(PARSENAME(@ip, 2) AS TINYINT),
     @oa = CAST(PARSENAME(@ip, 1) AS TINYINT)

    SET @ipInt = (@od*256*256*256) + (@oc*256*256) + (@ob*256) + @oa;
    RETURN @ipInt;
END

There is a small downside to this. Unfortunately the PARSENAME function is non-deterministic meaning it could potentially return different results when called across different servers.

SELECT OBJECTPROPERTYEX(OBJECT_ID('dbo.IP_2_INT'), 'IsDeterministic')

This was incorrectly documented in that past and so if it#s an issue for you then there is a work around using good old charindex & substrings available on the microsoft connect bug report page for it.
PARSENAME incorrectly documented as deterministic

~Eoin Campbell

Leave a Reply

Your email address will not be published. Required fields are marked *