How to compare software versions using SQL Server?

后端 未结 16 2060
清酒与你
清酒与你 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:05

    Two steps, first compare the left of the decimal point and after that compare the right.


    Possible solution:

    declare @v1 varchar(100) = '5.12'
    declare @v2 varchar(100) = '5.8'
    
    select case 
        when CONVERT(int, LEFT(@v1, CHARINDEX('.', @v1)-1)) < CONVERT(int, LEFT(@v2, CHARINDEX('.', @v2)-1)) then 'v2 is newer'
        when CONVERT(int, LEFT(@v1, CHARINDEX('.', @v1)-1)) > CONVERT(int, LEFT(@v2, CHARINDEX('.', @v2)-1)) then 'v1 is newer'
        when CONVERT(int, RIGHT(@v1, LEN(@v1) - CHARINDEX('.', @v1))) < CONVERT(int, RIGHT(@v2, LEN(@v2) - CHARINDEX('.', @v2))) then 'v2 is newer'
        when CONVERT(int, RIGHT(@v1, LEN(@v1) - CHARINDEX('.', @v1))) > CONVERT(int, RIGHT(@v2, LEN(@v2) - CHARINDEX('.', @v2))) then 'v1 is newer'
        else 'same!' end as 'Version Test'
    
    0 讨论(0)
  • 2020-12-03 08:05

    This is based on SeanW's answer but this solution allows for the following format [major].[minor].[build]. It maybe used for SQL 2K and when cursor is not an option.

    declare @v1 varchar(100) = '1.4.020'
    declare @v2 varchar(100) = '1.4.003'
    
    declare @v1_dot1_pos smallint   /*position - 1st version - 1st dot */
    declare @v1_dot2_pos smallint   /*position - 1st version - 2nd dot */
    declare @v2_dot1_pos smallint   /*position - 2nd version - 1st dot */
    declare @v2_dot2_pos smallint   /*position - 2nd version - 2nd dot */
    
    -------------------------------------------------
    -- get the pos of the first and second dots
    -------------------------------------------------
    SELECT 
    @v1_dot1_pos=CHARINDEX('.', @v1),
    @v2_dot1_pos=CHARINDEX('.', @v2),
    @v1_dot2_pos=charindex( '.', @v1, charindex( '.', @v1 ) + 1 ),
    @v2_dot2_pos=charindex( '.', @v2, charindex( '.', @v2 ) + 1 )
    
    
    -------------------------------------------------
    -- break down the parts
    -------------------------------------------------
    DECLARE @v1_major int, @v2_major int
    DECLARE @v1_minor int, @v2_minor int
    DECLARE @v1_build int, @v2_build int 
    
    SELECT 
        @v1_major = CONVERT(int,LEFT(@v1,@v1_dot1_pos-1)),
        @v1_minor = CONVERT(int,SUBSTRING(@v1,@v1_dot1_pos+1,(@v1_dot2_pos-@v1_dot1_pos)-1)),
        @v1_build = CONVERT(int,RIGHT(@v1,(LEN(@v1)-@v1_dot2_pos))),
        @v2_major = CONVERT(int,LEFT(@v2,@v2_dot1_pos-1)),
        @v2_minor = CONVERT(int,SUBSTRING(@v2,@v2_dot1_pos+1,(@v2_dot2_pos-@v2_dot1_pos)-1)),
        @v2_build = CONVERT(int,RIGHT(@v2,(LEN(@v2)-@v2_dot2_pos)))
    
    
    -------------------------------------------------
    -- return the difference
    -------------------------------------------------
    SELECT
        Case    
            WHEN @v1_major < @v2_major then 'v2 is newer'
            WHEN @v1_major > @v2_major then 'v1 is newer'
            WHEN @v1_minor < @v2_minor then 'v2 is newer'
            WHEN @v1_minor > @v2_minor then 'v1 is newer'
            WHEN @v1_build < @v2_build then 'v2 is newer'
            WHEN @v1_build > @v2_build then 'v1 is newer'
            ELSE '!Same'
        END
    
    0 讨论(0)
  • 2020-12-03 08:07
    declare @v1 varchar(100) = '5.12'
    declare @v2 varchar(100) = '5.8'
    
    select 
        case 
        when CONVERT(int, LEFT(@v1, CHARINDEX('.', @v1)-1)) < CONVERT(int, LEFT(@v2, CHARINDEX('.', @v2)-1)) then 'v2 is newer'
        when CONVERT(int, LEFT(@v1, CHARINDEX('.', @v1)-1)) > CONVERT(int, LEFT(@v2, CHARINDEX('.', @v2)-1)) then 'v1 is newer'
        when CONVERT(int, substring(@v1, CHARINDEX('.', @v1)+1, LEN(@v1))) < CONVERT(int, substring(@v2, CHARINDEX('.', @v2)+1, LEN(@v1))) then 'v2 is newer'
        when CONVERT(int, substring(@v1, CHARINDEX('.', @v1)+1, LEN(@v1))) > CONVERT(int, substring(@v2, CHARINDEX('.', @v2)+1, LEN(@v1))) then 'v1 is newer'
        else 'same!'
    
        end
    
    0 讨论(0)
  • 2020-12-03 08:07

    This recursive query would convert any '.'-separated version numbers into comparable strings left-padding each element to 10 characters thus allowing to compare versions with or without build number and accommodating for non-numeric characters:

    WITH cte (VersionNumber) AS (
      SELECT '1.23.456' UNION ALL
      SELECT '2.3'      UNION ALL
      SELECT '0.alpha-3'
      ),
      parsed (VersionNumber, Padded) AS (
      SELECT
        CAST(SUBSTRING(VersionNumber, CHARINDEX('.', VersionNumber) + 1, LEN(VersionNumber)) + '.' AS NVARCHAR(MAX)),
        CAST(RIGHT(REPLICATE('0', 10) + LEFT(VersionNumber, CHARINDEX('.', VersionNumber) - 1), 10) AS NVARCHAR(MAX))
      FROM cte
      UNION ALL
      SELECT
        SUBSTRING(VersionNumber, CHARINDEX('.', VersionNumber) + 1, LEN(VersionNumber)),
        Padded + RIGHT(REPLICATE('0', 10) + LEFT(VersionNumber, CHARINDEX('.', VersionNumber) - 1), 10)
      FROM parsed WHERE CHARINDEX('.', VersionNumber) > 0
      )
    SELECT Padded
    FROM parsed
    WHERE VersionNumber = ''
    ORDER BY Padded;
    

    Padded
    ------------------------------
    0000000000000alpha-3
    000000000100000000230000000456
    00000000020000000003
    
    0 讨论(0)
  • There was a very good solution from a duplicate question here: How to compare SQL strings that hold version numbers like .NET System.Version class?

    After playing with the query for a while, I learned that it was not able to compare the last part when there are 4 or more parts (say, if the version number was 1.2.3.4, it would always treat the last one as 0). I have fixed that issue as well as came up with another function to compare two version numbers.

    CREATE Function [dbo].[VersionNthPart](@version as nvarchar(max), @part as int) returns int as
    Begin
    
    Declare
        @ret as int = null,
        @start as int = 1,
        @end as int = 0,
        @partsFound as int = 0,
        @terminate as bit = 0
    
      if @version is not null
      Begin
        Set @ret = 0
        while @partsFound < @part
        Begin
          Set @end = charindex('.', @version, @start)
          If @end = 0 -- did not find the dot. Either it was last part or the part was missing.
          begin
            if @part - @partsFound > 1 -- also this isn't the last part so it must bail early.
            begin
                set @terminate = 1
            end
            Set @partsFound = @part
            SET @end = len(@version) + 1; -- get the full length so that it can grab the whole of the final part.
          end
          else
          begin
            SET @partsFound = @partsFound + 1
          end
          If @partsFound = @part and @terminate = 0
          begin
                Set @ret = Convert(int, substring(@version, @start, @end - @start))
          end
          Else
          begin
                Set @start = @end + 1
          end
        End
      End
      return @ret
    End
    GO
    
    CREATE FUNCTION [dbo].[CompareVersionNumbers]
    (
        @Source nvarchar(max),
        @Target nvarchar(max),
        @Parts int = 4
    )
    RETURNS INT
    AS
    BEGIN
    /*
    -1 : target has higher version number (later version)
    0 : same
    1 : source has higher version number (later version)
    */ 
        DECLARE @ReturnValue as int = 0;
        DECLARE @PartIndex as int = 1;
        DECLARE @SourcePartValue as int = 0;
        DECLARE @TargetPartValue as int = 0;
        WHILE (@PartIndex <= @Parts AND @ReturnValue = 0)
        BEGIN
            SET @SourcePartValue = [dbo].[VersionNthPart](@Source, @PartIndex);
            SET @TargetPartValue = [dbo].[VersionNthPart](@Target, @PartIndex);
            IF @SourcePartValue > @TargetPartValue
                SET @ReturnValue = 1
            ELSE IF @SourcePartValue < @TargetPartValue
                SET @ReturnValue = -1
            SET @PartIndex = @PartIndex + 1;
        END
        RETURN @ReturnValue
    END
    

    Usage/Test case:

    declare @Source as nvarchar(100) = '4.9.21.018'
    declare @Target as nvarchar(100) = '4.9.21.180'
    SELECT [dbo].[CompareVersionNumbers](@Source, @Target, DEFAULT) -- default version parts are 4
    
    SET @Source = '1.0.4.1'
    SET @Target = '1.0.1.8'
    SELECT [dbo].[CompareVersionNumbers](@Source, @Target, 4) -- typing out # of version parts also works
    
    SELECT [dbo].[CompareVersionNumbers](@Source, @Target, 2) -- comparing only 2 parts should be the same
    
    SET @Target = '1.0.4.1.5'
    SELECT [dbo].[CompareVersionNumbers](@Source, @Target, 4) -- only comparing up to parts 4 so they are the same
    SELECT [dbo].[CompareVersionNumbers](@Source, @Target, 5) -- now comparing 5th part which should indicate that the target has higher version number
    
    0 讨论(0)
  • I recommend to create a SQL CLR function:

    public partial class UserDefinedFunctions
    {
        [SqlFunction(Name = "CompareVersion")] 
        public static bool CompareVersion(SqlString x, SqlString y)
        {
            return Version.Parse(x) > Version.Parse(y);
        }
    }
    

    Notes:

    • SqlString has explicit cast to string.
    • Pass full version string as of a.b.c.d
    0 讨论(0)
提交回复
热议问题