sql-serveriplexicographic-ordering

Ms SQL function to make IP addresses lexicographically comparable


I found following code to stuff IP adresses with zeros to make it lexicographically comparable. The code works finde but I need it as a scalar function in SQL. Can you transform that in Code to create a scalar function that does the same? We use SQL Server 2008 R2 at the moment.

DECLARE @a as NVARCHAR(15)
SET @a = '2.18.4.14'

SELECT 
    @a = CASE 
        WHEN CHARINDEX('.',@a) < 4 
            THEN STUFF(@a,1,0,REPLICATE('0',4-CHARINDEX('.',@a,1))) 
        ELSE @a 
    END
, 
    @a = CASE 
        WHEN CHARINDEX('.',@a) < 8 
            THEN STUFF(@a,5,0,REPLICATE('0',8-CHARINDEX('.',@a, 5))) 
        ELSE @a 
    END
, 
    @a = CASE 
        WHEN CHARINDEX('.',@a) < 12 
            THEN STUFF(@a,9,0,REPLICATE('0',12-CHARINDEX('.',@a, 9))) 
        ELSE @a 
    END
, 
    @a = CASE 
        WHEN LEN(@a) < 15 
            THEN STUFF(@a,13,0,REPLICATE('0',15-LEN(@a))) 
        ELSE @a 
    END

SELECT @a

Can you explain how that code works? I understand what CHARINDEX, STUFF and REPLICATE do but I dont understand the steps how the IP adresse @a is modified by the CASE steps. Is ist a scalar value or a table?


Solution

  • Personally, I would use a Table-Value Function, not a Scalar function. Table-Value Function far out perform Scalar functions, and if you're going to be doing to against a dataset, then you'll notice a large speed increase.

    Firstly, this makes use of Jeff Moden's DelmitedSplit8k. If, however, you're using SQL Server 2016 then you can use STRING_SPLIT (I don't what version your using). Then the function is as simple as:

    CREATE FUNCTION Lexicon_IP_fn (@IPAddress VARCHAR(15))
    RETURNS TABLE AS
    RETURN 
    (
        SELECT CONVERT(varchar(15),STUFF((SELECT '.' + RIGHT('000' + DS.Item,3)
                                          FROM DelimitedSplit8K(@IPAddress, '.') DS
                                          FOR XML PATH('')),1,1,'')) AS Lexicon_IP
    );
    

    You can call the function using CROSS APPLY. For example:

    WITH VTE AS (
        SELECT *
        FROM (VALUES ('1.1.1.1'),('123.123.1.42'),('127.0.0.1')) V([IP]))
    SELECT [IP], LIP.Lexicon_IP
    FROM VTE
         CROSS APPLY Lexicon_IP_fn([IP]) LIP;
    

    Which returns:

    IP           Lexicon_IP
    ------------ ---------------
    1.1.1.1      001.001.001.001
    123.123.1.42 123.123.001.042
    127.0.0.1    127.000.000.001