Как сравнить версии программного обеспечения с помощью 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.
Ответы
Ответ 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);
}
}
Примечания:
- SqlString имеет явное приведение в строку.
- Пропустить полную версию строки с
a.b.c.d
Ответ 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