I have firmware version strings into my table (like \"4.2.2\" or \"4.2.16\")
How can I compare, select or sort them ?
I cannot use standard strings compariso
I've created a flexible SQL-only solution based on the excellent answer of Salman A above:
In this logic, I compare the first 4 version-segments. When the version string has more segments, the tailing ones are ignored.
The code fetches the id and ver columns from a table and then "sanitizes" the ver value to always contain 3 dots - this sanitized version is returned by the sane_ver field.
That sanitized version is then split into 4 integer values, each representing one version segment. You can compare or sort the results based on those 4 integers.
SELECT
id,
ver,
SUBSTRING_INDEX(sane_ver, '.', 1) + 0 AS ver1,
SUBSTRING_INDEX(SUBSTRING_INDEX(sane_ver, '.', 2), '.', -1) + 0 AS ver2,
SUBSTRING_INDEX(SUBSTRING_INDEX(sane_ver, '.', 3), '.', -1) + 0 AS ver3,
SUBSTRING_INDEX(SUBSTRING_INDEX(sane_ver, '.', 4), '.', -1) + 0 AS ver4
FROM (
SELECT
id,
ver,
CONCAT(
ver,
REPEAT('.0', 3 - CHAR_LENGTH(ver) + CHAR_LENGTH(REPLACE(ver, '.', '')))
) AS sane_ver
FROM (
SELECT id, ver FROM some_table
) AS raw_data
) AS sane_data
Here's a full query with some sample data and a filter that returns only versions that are lower than 1.2.3.4
SELECT
id,
ver,
SUBSTRING_INDEX(sane_ver, '.', 1) + 0 AS ver1,
SUBSTRING_INDEX(SUBSTRING_INDEX(sane_ver, '.', 2), '.', -1) + 0 AS ver2,
SUBSTRING_INDEX(SUBSTRING_INDEX(sane_ver, '.', 3), '.', -1) + 0 AS ver3,
SUBSTRING_INDEX(SUBSTRING_INDEX(sane_ver, '.', 4), '.', -1) + 0 AS ver4
FROM (
SELECT
id,
ver,
CONCAT(
ver,
REPEAT('.0', 3 - CHAR_LENGTH(ver) + CHAR_LENGTH(REPLACE(ver, '.', '')))
) AS sane_ver
FROM (
SELECT 1 AS id, '1' AS ver UNION
SELECT 2, '1.1' UNION
SELECT 3, '1.2.3.4.5' UNION
SELECT 4, '1.01' UNION
SELECT 5, '1.01.03' UNION
SELECT 6, '1.01.04a' UNION
SELECT 7, '1.01.04' UNION
SELECT 8, '1.01.04b' UNION
SELECT 9, '1.01.1.9.2.1.0' UNION
SELECT 10, '1.11' UNION
SELECT 11, '1.2' UNION
SELECT 12, '1.2.0' UNION
SELECT 13, '1.2.1' UNION
SELECT 14, '1.2.11' UNION
SELECT 15, '1.2.2' UNION
SELECT 16, '2.0' UNION
SELECT 17, '2.0.1' UNION
SELECT 18, '11.1.1' UNION
SELECT 19, '2020.11.18.11'
) AS raw_data
) AS sane_data
HAVING
ver1 <= 1
AND (ver2 <= 2 OR ver1 < 1)
AND (ver3 <= 3 OR ver2 < 2 OR ver1 < 1)
AND (ver4 < 4 OR ver3 < 3 OR ver2 < 2 OR ver1 < 1)
Note how this logic is different than the original code by Salman A:
The original answer uses CAST AS DECIMAL() which converts 1.02 to 1.020, and 1.1.0 to 1.100
→ That compares 1.02.0 to be lower than 1.1.0 (which is wrong, in my understanding)
The code in this answer converts 1.02 to the integers 1, 2, and 1.1 to the integers 1, 1
→ That compares 1.1.0 to be lower than 1.02.0
Also, both our solutions completely ignore any non-numeric characters, treating 1.2-alpha to be equal to 1.2.0.