MySQL 查询 - 比较版本号
MySQL query - compare version numbers
我的一个 SQL 表中有一个存储版本号的字段,例如 '3.4.23'
或 '1.224.43'
。
有没有办法为此字段使用大于条件?
SELECT * FROM versions WHERE version_number > '2.1.27'
有多个条件,这有点痛苦。这是一种蛮力方法:
where substring_index(version_number, '.', 1) + 0 > 2 or
(substring_index(version_number, '.', 1) = '2' and
substring_index(version_number, '.', 2) + 0 > 2.1
) or
(substring_index(version_number, '.', 2) = '2.1' and
substring_index(version_number, '.', -1) + 0 > 27
)
注意:right-hand 端可以使用相同的 substring_index()
表达式,但使用常量可以使逻辑更简单。
虽然可以编写比较版本号的函数,但这是解决问题的正确方法吗?比较 f(x) 和 f(y) 不能被索引。如果你知道版本号的任何部分永远不会超过,比如说,4 位数字,那么你可以创建一个额外的字段来保存用 0 填充的值(或者在 Mariadb 上,使用虚拟列),它可以被索引,例如2.1.27
将变为“000200010027”。
如果您不再尝试使用这样的编号模式而只使用整数或日期时间,事情就会简单得多。如果您必须坚持使用此编号,请考虑将数据拆分为 3 列。
为了快速破解,如果您知道版本号总是有 3 个组件并且每个组件总是小于 256,那么您可以...
SELECT *
FROM versions
WHERE INET_ATON(CONCAT(version_number, '.0')) > INET_ATON('2.1.27.0');
感谢@symcbean 和@gordon-linoff 的提示,我的最终查询如下所示:
SELECT *
FROM versions WHERE CONCAT(
LPAD(SUBSTRING_INDEX(SUBSTRING_INDEX(version_number, '.', 1), '.', -1), 10, '0'),
LPAD(SUBSTRING_INDEX(SUBSTRING_INDEX(version_number, '.', 2), '.', -1), 10, '0'),
LPAD(SUBSTRING_INDEX(SUBSTRING_INDEX(version_number, '.', 3), '.', -1), 10, '0')
) > CONCAT(LPAD(2,10,'0'), LPAD(1,10,'0'), LPAD(27,10,'0'));
这允许每个组件的长度最多为 10 位。
它改变了这个:
X.XX.XXX > 2.1.27
对此:
'000000000X00000000XX0000000XXX' > '000000000200000000010000000027'
@Adam 如果有像 v1 或 v1.2 这样的短版本号,最好先连接 version_number
,比如 CONCAT(version_number, '.0.0')
我使用以下适用于任何小于 255 的版本号:
SELECT * FROM versions
WHERE INET_ATON(SUBSTRING_INDEX(CONCAT(version, '.0.0.0'), '.', 4)) > INET_ATON(SUBSTRING_INDEX(CONCAT('2.1.27', '.0.0.0'), '.', 4));
我的一个 SQL 表中有一个存储版本号的字段,例如 '3.4.23'
或 '1.224.43'
。
有没有办法为此字段使用大于条件?
SELECT * FROM versions WHERE version_number > '2.1.27'
有多个条件,这有点痛苦。这是一种蛮力方法:
where substring_index(version_number, '.', 1) + 0 > 2 or
(substring_index(version_number, '.', 1) = '2' and
substring_index(version_number, '.', 2) + 0 > 2.1
) or
(substring_index(version_number, '.', 2) = '2.1' and
substring_index(version_number, '.', -1) + 0 > 27
)
注意:right-hand 端可以使用相同的 substring_index()
表达式,但使用常量可以使逻辑更简单。
虽然可以编写比较版本号的函数,但这是解决问题的正确方法吗?比较 f(x) 和 f(y) 不能被索引。如果你知道版本号的任何部分永远不会超过,比如说,4 位数字,那么你可以创建一个额外的字段来保存用 0 填充的值(或者在 Mariadb 上,使用虚拟列),它可以被索引,例如2.1.27
将变为“000200010027”。
如果您不再尝试使用这样的编号模式而只使用整数或日期时间,事情就会简单得多。如果您必须坚持使用此编号,请考虑将数据拆分为 3 列。
为了快速破解,如果您知道版本号总是有 3 个组件并且每个组件总是小于 256,那么您可以...
SELECT *
FROM versions
WHERE INET_ATON(CONCAT(version_number, '.0')) > INET_ATON('2.1.27.0');
感谢@symcbean 和@gordon-linoff 的提示,我的最终查询如下所示:
SELECT *
FROM versions WHERE CONCAT(
LPAD(SUBSTRING_INDEX(SUBSTRING_INDEX(version_number, '.', 1), '.', -1), 10, '0'),
LPAD(SUBSTRING_INDEX(SUBSTRING_INDEX(version_number, '.', 2), '.', -1), 10, '0'),
LPAD(SUBSTRING_INDEX(SUBSTRING_INDEX(version_number, '.', 3), '.', -1), 10, '0')
) > CONCAT(LPAD(2,10,'0'), LPAD(1,10,'0'), LPAD(27,10,'0'));
这允许每个组件的长度最多为 10 位。
它改变了这个:
X.XX.XXX > 2.1.27
对此:
'000000000X00000000XX0000000XXX' > '000000000200000000010000000027'
@Adam 如果有像 v1 或 v1.2 这样的短版本号,最好先连接 version_number
,比如 CONCAT(version_number, '.0.0')
我使用以下适用于任何小于 255 的版本号:
SELECT * FROM versions
WHERE INET_ATON(SUBSTRING_INDEX(CONCAT(version, '.0.0.0'), '.', 4)) > INET_ATON(SUBSTRING_INDEX(CONCAT('2.1.27', '.0.0.0'), '.', 4));