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
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]
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'
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
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