How to compare software versions using SQL Server?

后端 未结 16 2061
清酒与你
清酒与你 2020-12-03 07:30

When trying to compare software versions 5.12 to 5.8, version 5.12 is newer, however mathematically 5.12 is less than 5.8. How would I compare the two versions so that a new

相关标签:
16条回答
  • 2020-12-03 08:19

    Maybe converting build number to a value can help to understand the hierarchy between build versions.

    DECLARE @version VARCHAR(25), @dot1 AS TINYINT, @dot2 AS TINYINT, @dot3 AS TINYINT, @MaxPower AS TINYINT, @Value AS BIGINT
    SELECT @version = CAST(SERVERPROPERTY('ProductVersion') AS VARCHAR) --'14.0.1000.169' --'10.50.1600'
    SELECT @dot1 = CHARINDEX('.', @version, 1)
    SELECT @dot2 = CHARINDEX('.', @version, @dot1 + 1)
    SELECT @dot3 = CHARINDEX('.', @version, @dot2 + 1)
    SELECT @dot3 = CASE
        WHEN @dot3 = 0 THEN LEN(@version) + 1
        ELSE @dot3
    END
    
    SELECT @MaxPower = MAX(DotColumn) FROM (VALUES (@dot1-1), (@dot2-@dot1-1), (@dot3-@dot2-1)) AS DotTable(DotColumn)
    SELECT @Value = POWER(10, @MaxPower)
    --SELECT @version, @dot1, @dot2, @dot3, @MaxPower, @Value
    
    SELECT 
    --  @version AS [Build], 
        CAST(LEFT(@version, @dot1-1) AS INT) * POWER(@Value, 3) +
        CAST(SUBSTRING(@version, @dot1+1, @dot2-@dot1-1) AS INT) * POWER(@Value, 2) +
        CAST(SUBSTRING(@version, @dot2+1, @dot3-@dot2-1) AS INT) * @Value +
        CASE
            WHEN @dot3 = LEN(@version)+1 THEN CAST(0 AS INT)
            ELSE CAST(SUBSTRING(@version, @dot3+1, LEN(@version)-@dot3) AS INT)
        END AS [Value]
    
    0 讨论(0)
  • 2020-12-03 08:20

    I have created (with inspiration from Eva Lacy (above)), this function:

    CREATE or alter function dbo.IsVersionNewerThan
    (
        @Source nvarchar(max),
        @Target nvarchar(max)
    )
    RETURNS table
    as 
    /*
    -1 : target has higher version number (later version)
    0 : same
    1 : source has higher version number (later version)
    
    test harness:
    ; WITH tmp
    AS
    (
        SELECT '1.0.0.5' AS Version
        UNION ALL SELECT '0.0.0.0'
        UNION ALL SELECT '1.5.0.6'
        UNION ALL SELECT '2.0.0'
        UNION ALL SELECT '2.0.0.0'
        UNION ALL SELECT '2.0.1.1'
        UNION ALL SELECT '15.15.1323.22'
        UNION ALL SELECT '15.15.622.55'
    )
    SELECT tmp.version, isGreather from tmp
    outer apply (select * from dbo.IsVersionNewerThan(tmp.Version, '2.0.0.0')) as IsG
    
    */ 
        return (
            select CASE 
                when cast('/' + @Source + '/' as hierarchyid) > cast('/' + @Target + '/' as hierarchyid) THEN 1 
                when @Source = @Target then 0
                else -1 
            end as IsGreather
        )
    go
    

    The test script is included as a comment. It works, as long as you do not have versions like '1.5.06.2' (note the zero).
    SQL Server thinks this function has is_inlineable = 1, which bodes well for the performance.

    Then my SQL code can look like this:

    declare @version varchar(10) = '2.30.1.12'
    set @version = '2.30.1.1'
    if exists(select * from dbo.IsVersionNewerThan(@version,'2.30.1.12') where IsGreather >= 0)
    BEGIN
        print 'yes'
    end
    else print 'no'
    
    0 讨论(0)
  • 2020-12-03 08:30

    You could use hierarchyid Which you can use by putting a / at the end and start of the string and casting it

    e.g.

    SELECT CASE WHEN cast('/5.12/' as hierarchyid) > cast('/5.8/' as hierarchyid) THEN 'Y' ELSE 'N' END

    That returns a Y

    0 讨论(0)
  • 2020-12-03 08:30

    Here is what I did by modifying some code I found on StackOverflow and writing some myself. This is version 1 of the code so please let me know what you think. Usage examples and test cases are in the code comments.

    First create this function if not using SQL 2016 or greater and you do not have access to STRING_SPLIT:

    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    -- =============================================
    -- Author:      <Author,,Name>
    -- Create date: <Create Date,,>
    -- Description: modified from https://stackoverflow.com/questions/10914576/t-sql-split-string/42000063#42000063
    -- =============================================
    CREATE FUNCTION [dbo].[SplitStringToRows]
    (   
        @List VARCHAR(4000) 
        , @Delimiter VARCHAR(50)
    )
    RETURNS TABLE 
    AS
    RETURN 
    (
        --For testing
        -- SELECT * FROM SplitStringToRows ('1.0.123','.')
        -- DECLARE @List VARCHAR(MAX) = '1.0.123', @Delimiter VARCHAR(50) = '.';
    
        WITH Casted AS
        (
            SELECT CAST(N'<x>' + REPLACE((SELECT REPLACE(@List,@Delimiter,N'§§Split$me$here§§') AS [*] FOR XML PATH('')),N'§§Split$me$here§§',N'</x><x>') + N'</x>' AS XML) AS SplitMe
        )
        SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS [Index]
        , x.value(N'.',N'nvarchar(max)') AS Part 
        FROM Casted
        CROSS APPLY SplitMe.nodes(N'/x') AS A(x)
    )
    

    Then create this function:

    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    -- =============================================
    -- Author:      Soenhay
    -- Create date: 7/1/2017
    -- Description: Returns -1 if VersionStringA is less than VersionStringB.
    --              Returns 0 if VersionStringA equals VersionStringB.
    --              Returns 1 if VersionSTringA is greater than VersionStringB.
    -- =============================================
    CREATE FUNCTION dbo.CompareVersionStrings
    (   
        @VersionStringA VARCHAR(50)
        ,@VersionStringB VARCHAR(50)
    )
    RETURNS TABLE 
    AS
    RETURN 
    (
        --CurrentVersion should be of the form:
        --major.minor[.build[.revision]] 
        --This is the same as the versioning system used in c#.
        --For applications the build and revision numbers will by dynamically set based on the current date and time of the build. 
        --Example: [assembly: AssemblyFileVersion("1.123.*")]//http://stackoverflow.com/questions/15505841/the-version-specified-for-the-file-version-is-not-in-the-normal-major-minor-b
        --Each component should be between 0 and 65534 ( UInt16.MaxValue - 1 )
        --Max version number would be 65534.65534.65534.65534
    
        --For Testing 
        -- SELECT * FROM dbo.CompareVersionStrings('', '')
        -- SELECT * FROM dbo.CompareVersionStrings('asdf.asdf', 'asdf.asdf') --returns 0
        -- SELECT * FROM dbo.CompareVersionStrings('asdf', 'fdas') --returns -1 
        -- SELECT * FROM dbo.CompareVersionStrings('zasdf', 'fdas') --returns 1 
        -- SELECT * FROM dbo.CompareVersionStrings('1.0.123.123', '1.1.123.123')  --Should return -1
        -- SELECT * FROM dbo.CompareVersionStrings('1.0.123.123', '1.0.123.123')  --Should return 0
        -- SELECT * FROM dbo.CompareVersionStrings('1.1.123.123', '1.0.123.123')  --Should return 1
        -- SELECT * FROM dbo.CompareVersionStrings('1.0.123.123', '1.0.124.123')  --Should return -1
        -- SELECT * FROM dbo.CompareVersionStrings('1.0.124.123', '1.0.123.123')  --Should return 1
        -- SELECT * FROM dbo.CompareVersionStrings('1.0.123.123', '1.0.123.124')  --Should return -1
        -- SELECT * FROM dbo.CompareVersionStrings('1.0.123.124', '1.0.123.123')  --Should return 1
        -- SELECT * FROM dbo.CompareVersionStrings('1.0', '1.1')  --Should return -1
        -- SELECT * FROM dbo.CompareVersionStrings('1.0', '1.0')  --Should return 0
        -- SELECT * FROM dbo.CompareVersionStrings('1.1', '1.0')  --Should return 1
        -- Declare @VersionStringA VARCHAR(50) = '' ,@VersionStringB VARCHAR(50) = '' ;
        -- Declare @VersionStringA VARCHAR(50) = '1.0.123.123' ,@VersionStringB VARCHAR(50) = '1.1.123.123' ;
        -- Declare @VersionStringA VARCHAR(50) = '1.1.123.123' ,@VersionStringB VARCHAR(50) = '1.1.123.123' ;
        -- Declare @VersionStringA VARCHAR(50) = '1.2.123.123' ,@VersionStringB VARCHAR(50) = '1.1.123.123' ;
        -- Declare @VersionStringA VARCHAR(50) = '1.1.123' ,@VersionStringB VARCHAR(50) = '1.1.123.123' ;
        -- Declare @VersionStringA VARCHAR(50) = '1.1.123.123' ,@VersionStringB VARCHAR(50) = '1.1.123' ;
        -- Declare @VersionStringA VARCHAR(50) = '1.1' ,@VersionStringB VARCHAR(50) = '1.1' ;
        -- Declare @VersionStringA VARCHAR(50) = '1.2' ,@VersionStringB VARCHAR(50) = '1.1' ;
        -- Declare @VersionStringA VARCHAR(50) = '1.1' ,@VersionStringB VARCHAR(50) = '1.2' ;
    
        WITH 
        Indexes AS
        (
            SELECT 1 AS [Index]
                , 'major' AS Name
            UNION
            SELECT 2
                , 'minor'
            UNION
            SELECT 3
                , 'build'
            UNION
            SELECT 4
                , 'revision'
        )
        , SplitA AS
        (
            SELECT * FROM dbo.SplitStringToRows(@VersionStringA, '.')
        )
        , SplitB AS
        (
            SELECT * FROM dbo.SplitStringToRows(@VersionStringB, '.')
        )
        SELECT
            CASE WHEN major = 0 THEN
                    CASE WHEN minor = 0 THEN
                                        CASE WHEN build = 0 THEN
                                                            CASE WHEN revision = 0 THEN 0
                                                            ELSE revision END
                                            ELSE build END
                        ELSE minor END
                ELSE major END AS Compare
        FROM
        (
            SELECT 
                 MAX(CASE WHEN [Index] = 1 THEN Compare ELSE NULL END) AS major
                ,MAX(CASE WHEN [Index] = 2 THEN Compare ELSE NULL END) AS minor
                ,MAX(CASE WHEN [Index] = 3 THEN Compare ELSE NULL END) AS build
                ,MAX(CASE WHEN [Index] = 4 THEN Compare ELSE NULL END) AS revision
            FROM(
                SELECT [Index], Name, 
                    CASE WHEN A = B THEN 0
                        WHEN A < B THEN -1
                        WHEN A > B THEN 1
                        END AS Compare
                FROM
                (
                    SELECT 
                         i.[Index]
                        ,i.Name
                        ,ISNULL(a.Part, 0) AS A
                        ,ISNULL(b.Part, 0) AS B
                    FROM Indexes i
                        LEFT JOIN SplitA a
                    ON  a.[Index] = i.[Index]
                        LEFT JOIN SplitB b
                    ON  b.[Index] = i.[Index]
                ) q1
            ) q2
        ) q3
    
    )
    GO
    
    0 讨论(0)
提交回复
热议问题