Подтвердить что ты не робот

Как сравнить версии программного обеспечения с помощью SQL Server?

При попытке сравнить версии программного обеспечения с 5.12 по 5.8 версия 5.12 новее, однако математически 5.12 меньше 5.8. Как бы сравнить две версии, чтобы более новая версия вернула "Y"?

SELECT CASE WHEN 5.12 > 5.8 THEN 'Y' ELSE 'N' END

Возможные решения

  • Добавьте 0 после десятичного числа в 5.8, чтобы он сравнивал 5.08-5.12, однако, похоже, для этого потребуется немного кода.
  • Просто сравните значения после десятичной (т.е. 12 > 8), однако это не удается, когда версия переместится в 6.0.
  • Используйте обратную логику и предположим, что если 5.12 меньше 5.8, то для возврата "Y". Я считаю, что это закончилось бы неудачей, когда версия переместится в 6.0.
4b9b3361

Ответ 1

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

Ответ 2

Я рекомендую создать функцию CLR SQL:

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

Примечания:

Ответ 3

Было очень хорошее решение по дублирующему вопросу: Как сравнить строки SQL, содержащие номера версий, такие как класс .NET System.Version?

Поиграв с запросом какое-то время, я узнал, что он не смог сравнить последнюю часть, когда есть 4 или более частей (например, если номер версии был 1.2.3.4, он всегда будет обрабатывать последний как 0). Я исправил эту проблему, а также придумал другую функцию для сравнения двух номеров версий.

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

Использование/тестовый пример:

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

Ответ 4

Два шага, сначала сравните левую часть десятичной точки и после этого сравните правую.


Возможное решение:

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'

Ответ 5

Я столкнулся с этим при попытке фильтровать строки SQL на основе семантического управления версиями. Мое решение было несколько иным, поскольку я хотел сохранить строки конфигурации, помеченные семантическим номером версии, а затем выбрать строки, совместимые с текущей версией нашего программного обеспечения.

Предположения:

  • Мое программное обеспечение будет включать настройку конфигурации, содержащую текущий номер версии
  • Строки конфигурации, основанные на данных, будут содержать номер версии min
  • Мне нужно иметь возможность выбирать строки конфигурации, где min <= current.

Примеры:

  • Версия 1.0.0 должна включать: 1.0.0, 1.0.0- *, 1.0.0-beta.1
  • Версия 1.0.0 должна исключать: 1.0.1, 1.1.0, 2.0.0
  • Версия 1.1.0-beta.2 должна включать: 1.0.0, 1.0.1, 1.1.0-beta.1, 1.1.0-beta.2
  • Версия 1.1.0-beta.2 должна исключать: 1.1.0, 1.1.1, 1.2.0, 2.0.0, 1.1.1-beta.1

MSSQL UDF:

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-тесты включают в себя:

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

Ответ 6

Не хранить в строке то, что не является строкой. Альтернатива создает собственный тип данных (в С# - допустимый на некоторое время), который хранит версии как последовательность байтов и реализует правильную логику сравнения.

Ответ 7

Как было предложено AF, вы можете сравнить часть int, а затем десятичную часть. Из всех полученных ответов есть еще один способ сделать это, используя parseame. Вы можете попробовать что-то вроде этого

 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

Ответ 8

Вы не говорите об этом в вопросе, но ваш комментарий в ответ Tomtom говорит, что вы сохраняете номера версий как [десятичные числа] [d]. Я предполагаю, что у вас есть таблица вроде этого:

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

Следующий запрос - попытка ранжировать версии по порядку, в котором они будут выпущены:

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

Он производит следующий результирующий набор:

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

Это не то, чего мы ожидаем. Версия 5.8 была выпущена до версии 5.12!

Разделите номер версии на основные и второстепенные компоненты, чтобы правильно ранжировать номера версий. Один из способов сделать это - преобразовать десятичное значение в строку и разделить на период. Синтаксис T-SQL для этого является уродливым (язык не предназначен для обработки строк):

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;

Но он обеспечивает ожидаемый результат:

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

Как Tomtom ответил, десятичный код не является хорошим типом для хранения номера версии. Было бы лучше сохранить номер версии в двух положительных целочисленных столбцах, один из которых содержит номер основной версии, а другой - младший номер версии.

Ответ 9

Это основано на ответе SeanW, но это решение позволяет использовать следующий формат [major]. [minor]. [build]. Возможно, он используется для SQL 2K, и когда курсор не является опцией.

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,(@[email protected]_dot1_pos)-1)),
    @v1_build = CONVERT(int,RIGHT(@v1,(LEN(@v1)[email protected]_dot2_pos))),
    @v2_major = CONVERT(int,LEFT(@v2,@v2_dot1_pos-1)),
    @v2_minor = CONVERT(int,SUBSTRING(@v2,@v2_dot1_pos+1,(@[email protected]_dot1_pos)-1)),
    @v2_build = CONVERT(int,RIGHT(@v2,(LEN(@v2)[email protected]_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

Ответ 10

Решение, которое было реализовано:

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

Ответ 11

Вы можете использовать hierarchyid Который вы можете использовать, положив / в конце и начале строки и произведя ее

например.

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

Это возвращает Y

Ответ 12

Вот что я сделал, изменив код, который я нашел на StackOverflow, и сам написал. Это версия 1 кода, поэтому, пожалуйста, дайте мне знать, что вы думаете. Примеры использования и тестовые примеры содержатся в комментариях кода.

Сначала создайте эту функцию, если не используете SQL 2016 или выше, и у вас нет доступа к 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)
)

Затем создайте эту функцию:

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

Ответ 13

Этот рекурсивный запрос преобразует любые номера разделенных номеров. в разделенные строки, оставляя каждый элемент до 10 символов, что позволяет сравнивать версии с номером или без номера сборки и размещения для нечисловых символов:

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