How to compare software versions using SQL Server?

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

    I'll give you the most shortest answer of this.

    with cte as (
        select  7.11 as ver
        union all
        select 7.6
    )
    
    select top 1 ver from cte
          order by parsename(ver, 2), parsename(cast(ver as float), 1)
    
    0 讨论(0)
  • 2020-12-03 08:10

    As suggested by AF you can compare the int part and then the decimal part .Apart from all the answers given there is one more way to do it using parsename .You could try something like this

     case when cast(@var as int)>cast(@var2 as int) then 'Y' 
     when cast(PARSENAME(@var,1) as int) > cast(PARSENAME(@var2,1) as int) THEN 'Y'
    
    
     Declare @var float
     Declare @var2 float
     set @var=5.14
     set @var2=5.8
     Select case when cast(@var as int)>cast(@var2 as int) then 'Y' 
     when cast(PARSENAME(@var,1) as int)> cast(PARSENAME(@var2,1) as int) THEN 'Y'
     else 'N' END
    
    0 讨论(0)
  • 2020-12-03 08:15

    The solution that was implemented:

    CREATE FUNCTION [dbo].[version_compare]
    (
        @v1 VARCHAR(5), @v2 VARCHAR(5)
    )
    RETURNS tinyint
    AS
    BEGIN
        DECLARE @v1_int tinyint, @v1_frc tinyint, 
                @v2_int tinyint, @v2_frc tinyint, 
                @ResultVar tinyint
    
        SET @ResultVar = 0
    
        SET @v1_int = CONVERT(tinyint, LEFT(@v1, CHARINDEX('.', @v1) - 1))
        SET @v1_frc = CONVERT(tinyint, RIGHT(@v1, LEN(@v1) - CHARINDEX('.', @v1)))
        SET @v2_int = CONVERT(tinyint, LEFT(@v2, CHARINDEX('.', @v2) - 1))
        SET @v2_frc = CONVERT(tinyint, RIGHT(@v2, LEN(@v2) - CHARINDEX('.', @v2)))
    
        SELECT @ResultVar = CASE
            WHEN @v2_int > @v1_int THEN 2
            WHEN @v1_int > @v2_int THEN 1
            WHEN @v2_frc > @v1_frc THEN 2
            WHEN @v1_frc > @v2_frc THEN 1
        ELSE 0 END
    
        -- Return the result of the function
        RETURN @ResultVar
    END
    GO
    
    0 讨论(0)
  • 2020-12-03 08:17

    I encountered this when trying to filter SQL rows based on semantic versioning. My solution was a bit different, in that I wanted to store configuration rows tagged with a semantic version number and then select rows compatible with a running version of our software.

    Assumptions:

    • My software will include a configuration setting containing the current version number
    • Data-driven configuration rows will include a min version number
    • I need to be able to select configuration rows where min <= current.

    Examples:

    • Version 1.0.0 should include: 1.0.0, 1.0.0-*, 1.0.0-beta.1
    • Version 1.0.0 should exclude: 1.0.1, 1.1.0, 2.0.0
    • Version 1.1.0-beta.2 should include: 1.0.0, 1.0.1, 1.1.0-beta.1, 1.1.0-beta.2
    • Version 1.1.0-beta.2 should exclude: 1.1.0, 1.1.1, 1.2.0, 2.0.0, 1.1.1-beta.1

    The MSSQL UDF is:

    CREATE FUNCTION [dbo].[SemanticVersion] (
        @Version nvarchar(50)
    )
    RETURNS nvarchar(255)
    
    AS
    BEGIN
    
        DECLARE @hyphen int = CHARINDEX('-', @version)
        SET @Version = REPLACE(@Version, '*', ' ')
        DECLARE 
            @left nvarchar(50) = CASE @hyphen WHEN 0 THEN @version ELSE SUBSTRING(@version, 1, @hyphen-1) END,
            @right nvarchar(50) = CASE @hyphen WHEN 0 THEN NULL ELSE SUBSTRING(@version, @hyphen+1, 50) END,
            @normalized nvarchar(255) = '',
            @buffer int = 8
    
        WHILE CHARINDEX('.', @left) > 0 BEGIN
            SET @normalized = @normalized + CASE ISNUMERIC(LEFT(@left, CHARINDEX('.', @left)-1))
                WHEN 0 THEN LEFT(@left, CHARINDEX('.', @left)-1)
                WHEN 1 THEN REPLACE(STR(LEFT(@left, CHARINDEX('.', @left)-1), @buffer), SPACE(1), '0')
            END  + '.'
            SET @left = SUBSTRING(@left, CHARINDEX('.', @left)+1, 50)
        END
        SET @normalized = @normalized + CASE ISNUMERIC(@left)
            WHEN 0 THEN @left
            WHEN 1 THEN REPLACE(STR(@left, @buffer), SPACE(1), '0')
        END
    
        SET @normalized = @normalized + '-'
        IF (@right IS NOT NULL) BEGIN
            WHILE CHARINDEX('.', @right) > 0 BEGIN
                SET @normalized = @normalized + CASE ISNUMERIC(LEFT(@right, CHARINDEX('.', @right)-1))
                    WHEN 0 THEN LEFT(@right, CHARINDEX('.', @right)-1)
                    WHEN 1 THEN REPLACE(STR(LEFT(@right, CHARINDEX('.', @right)-1), @buffer), SPACE(1), '0')
                END  + '.'
                SET @right = SUBSTRING(@right, CHARINDEX('.', @right)+1, 50)
            END
            SET @normalized = @normalized + CASE ISNUMERIC(@right)
                WHEN 0 THEN @right
                WHEN 1 THEN REPLACE(STR(@right, @buffer), SPACE(1), '0')
            END
        END ELSE 
            SET @normalized = @normalized + 'zzzzzzzzzz'
    
        RETURN @normalized
    
    END
    

    SQL tests include:

    SELECT CASE WHEN dbo.SemanticVersion('1.0.0-alpha') < dbo.SemanticVersion('1.0.0-alpha.1') THEN 'Success' ELSE 'Failure' END
    SELECT CASE WHEN dbo.SemanticVersion('1.0.0-alpha.1') < dbo.SemanticVersion('1.0.0-alpha.beta') THEN 'Success' ELSE 'Failure' END
    SELECT CASE WHEN dbo.SemanticVersion('1.0.0-alpha.beta') < dbo.SemanticVersion('1.0.0-beta') THEN 'Success' ELSE 'Failure' END
    SELECT CASE WHEN dbo.SemanticVersion('1.0.0-beta') < dbo.SemanticVersion('1.0.0-beta.2') THEN 'Success' ELSE 'Failure' END
    SELECT CASE WHEN dbo.SemanticVersion('1.0.0-beta.2') < dbo.SemanticVersion('1.0.0-beta.11') THEN 'Success' ELSE 'Failure' END
    SELECT CASE WHEN dbo.SemanticVersion('1.0.0-beta.11') < dbo.SemanticVersion('1.0.0-rc.1') THEN 'Success' ELSE 'Failure' END
    SELECT CASE WHEN dbo.SemanticVersion('1.0.0-rc.1') < dbo.SemanticVersion('1.0.0') THEN 'Success' ELSE 'Failure' END
    
    
    SELECT CASE WHEN dbo.SemanticVersion('1.0.0-*') <= dbo.SemanticVersion('1.0.0') THEN 'Success' ELSE 'Failure' END
    SELECT CASE WHEN dbo.SemanticVersion('1.0.*') <= dbo.SemanticVersion('1.0.0') THEN 'Success' ELSE 'Failure' END
    SELECT CASE WHEN dbo.SemanticVersion('1.*') <= dbo.SemanticVersion('1.0.0') THEN 'Success' ELSE 'Failure' END
    SELECT CASE WHEN dbo.SemanticVersion('*') <= dbo.SemanticVersion('1.0.0') THEN 'Success' ELSE 'Failure' END
    
    SELECT CASE WHEN dbo.SemanticVersion('1.0.0-*') <= dbo.SemanticVersion('1.0.0') THEN 'Success' ELSE 'Failure' END
    SELECT CASE WHEN dbo.SemanticVersion('1.0.1-*') > dbo.SemanticVersion('1.0.0') THEN 'Success' ELSE 'Failure' END
    SELECT CASE WHEN dbo.SemanticVersion('1.0.1-*') <= dbo.SemanticVersion('1.0.1') THEN 'Success' ELSE 'Failure' END
    SELECT CASE WHEN dbo.SemanticVersion('1.1.*') > dbo.SemanticVersion('1.0.9') THEN 'Success' ELSE 'Failure' END
    SELECT CASE WHEN dbo.SemanticVersion('1.1.*') <= dbo.SemanticVersion('1.2.0') THEN 'Success' ELSE 'Failure' END
    SELECT CASE WHEN dbo.SemanticVersion('1.*') <= dbo.SemanticVersion('2.0.0') THEN 'Success' ELSE 'Failure' END
    SELECT CASE WHEN dbo.SemanticVersion('1.*') > dbo.SemanticVersion('0.9.9-beta-219') THEN 'Success' ELSE 'Failure' END
    SELECT CASE WHEN dbo.SemanticVersion('*') <= dbo.SemanticVersion('0.0.1-alpha-1') THEN 'Success' ELSE 'Failure' END
    
    0 讨论(0)
  • 2020-12-03 08:17

    Do not store in a string what is not a string. Alternative is creating your own data type (in C# - allowed for some time) that stored the versions as a sequence of bytes and implements proper comparison logic.

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

    You don't say so in the question, but your comment under Tomtom's answer suggests you are storing the version numbers as [decimals][d]. I guess that you have a table like this:

    CREATE TABLE ReleaseHistory (
      VersionNumber DECIMAL(6,3) NOT NULL
    );
    GO
    
    INSERT INTO ReleaseHistory (
      VersionNumber
    )
    VALUES
      (5.12),
      (5.8),
      (12.34),
      (3.14),
      (0.78),
      (1.0);
    GO
    

    The following query is an attempt to rank versions by the order in which they would be released:

    SELECT
      VersionNumber,
      RANK() OVER (ORDER BY VersionNumber) AS ReleaseOrder
    FROM ReleaseHistory;
    

    It produces the following result set:

    VersionNumber                           ReleaseOrder
    --------------------------------------- --------------------
    0.780                                   1
    1.000                                   2
    3.140                                   3
    5.120                                   4
    5.800                                   5
    12.340                                  6
    

    This is not what we expect. Version 5.8 was released before version 5.12!

    Split the version number into its major and minor components to rank the version numbers properly. One way to do this is to convert the decimal value to a string and split on the period. The T-SQL syntax for this is ugly (the language is not designed for string processing):

    WITH VersionStrings AS (
      SELECT CAST(VersionNumber AS VARCHAR(6)) AS VersionString
      FROM ReleaseHistory
    ),
    VersionNumberComponents AS (
      SELECT
        CAST(SUBSTRING(VersionString, 1, CHARINDEX('.', VersionString) - 1) AS INT) AS MajorVersionNumber,
        CAST(SUBSTRING(VersionString, CHARINDEX('.', VersionString) + 1, LEN(VersionString) - CHARINDEX('.', VersionString)) AS INT) AS MinorVersionNumber
      FROM VersionStrings
    )
    SELECT
      CAST(MajorVersionNumber AS VARCHAR(3)) + '.' + CAST(MinorVersionNumber AS VARCHAR(3)) AS VersionString,
      RANK() OVER (ORDER BY MajorVersionNumber, MinorVersionNumber) AS ReleaseOrder
    FROM VersionNumberComponents;
    

    But it provides the expected result:

    VersionString ReleaseOrder
    ------------- --------------------
    0.780         1
    1.0           2
    3.140         3
    5.120         4
    5.800         5
    12.340        6
    

    As Tomtom replied, decimal is a not a good type to store a version number. It would be better to store the version number in two positive integer columns, one containing the major version number and the other containing the minor version number.

    0 讨论(0)
提交回复
热议问题