当列可以以字母开头或结尾时,使用MySQL对varchar列进行数字排序并强制转换为无符号
我遇到了一些事情,我真的不知道该怎么处理。我正在建立一个数据库来存储运动卡上的信息,当我想查看某些卡时,我在排序方面遇到了一些问题 作为背景,每张卡(数据库中的一行)都有关于年份、设置卡的来源、卡上的玩家和卡号的信息(还有更多的信息,但这就是所有相关信息)。当我看到结果时,我希望事情按年份排序,然后是集合,然后是玩家,然后是卡号。除了卡号之外,其他一切都很正常,因为年份只是一个整数,而且set和player都是varchar,所以很容易对它们进行排序。然而,卡号正是我遇到的一些问题 卡号列是varchar,因为卡号可以包括字母、数字和破折号。最常见的情况是,卡号是一个直数字(即1、2、3、4、5)、直字母(Ex-a、Ex-B、Ex-C)、一个数字后接一个字母(1a、1b、2、3a、3b、3c),或者一个字母后接一个数字(A1、A2、A3、A4、A5)。这是我当前设置SQL字符串排序部分的方式:当列可以以字母开头或结尾时,使用MySQL对varchar列进行数字排序并强制转换为无符号,mysql,sql,sorting,natural-sort,Mysql,Sql,Sorting,Natural Sort,我遇到了一些事情,我真的不知道该怎么处理。我正在建立一个数据库来存储运动卡上的信息,当我想查看某些卡时,我在排序方面遇到了一些问题 作为背景,每张卡(数据库中的一行)都有关于年份、设置卡的来源、卡上的玩家和卡号的信息(还有更多的信息,但这就是所有相关信息)。当我看到结果时,我希望事情按年份排序,然后是集合,然后是玩家,然后是卡号。除了卡号之外,其他一切都很正常,因为年份只是一个整数,而且set和player都是varchar,所以很容易对它们进行排序。然而,卡号正是我遇到的一些问题 卡号列是va
order by year desc, cardset asc, subset asc, cast(cardNum as unsigned) asc;
这是处理大多数事情的好方法。但我遇到的问题是,当一组卡片的卡号中有相同的字母,然后有一个数字。我希望排序基本上忽略前导字母,然后只按数字排序。但是,有时它不能正确地执行此操作,特别是当有超过5张的卡片需要排序时
具体而言,它错误地将一些具有以下卡号的卡按以下顺序排序:
- BCP61
- BCP97
- BCP32
- BCP135
- BCP32
- BCP61
- BCP97
- BCP135
- 2016年鲍曼52
- 2016年鲍曼54
- 2016年鲍曼147
- 2016年鲍曼铬合金展望BCP32
- 2016年鲍曼铬合金展望BCP61
- 2016年鲍曼铬合金展望BCP97
- 2016年鲍曼铬合金展望BCP125
- 2016年排名第一
- 2016年Topps 2a
- 2016年Topps 2b
- 2016年排名第三
- 2016年鲍曼52
- 2016年鲍曼54
- 2016年鲍曼147
- 2016年鲍曼铬合金展望BCP62
- 2016年鲍曼铬合金展望BCP97
- 2016年鲍曼铬合金展望BCP32
- 2016年鲍曼铬合金展望BCP125
- 2016年排名第一
- 2016年Topps 2a
- 2016年Topps 2b
- 2016年排名第三
order by year desc, cardset asc, subset asc, length(cardNum), cardNum asc
这确实解决了我上面描述的问题,但弄乱了我示例中的“Topps”部分——它将把字母跟在数字后面的卡片放在最后,不管发生什么。这是我得到的排序:
- 2016年鲍曼52
- 2016年鲍曼54
- 2016年鲍曼147
- 2016年鲍曼铬合金展望BCP32
- 2016年鲍曼铬合金展望BCP61
- 2016年鲍曼铬合金展望BCP97
- 2016年鲍曼铬合金展望BCP125
- 2016年排名第一
- 2016年排名第三
- 2016年Topps 2a
- 2016年Topps 2b
length()
技巧:
order by year desc, cardset asc, subset asc,
length(cardNum),
cardNum asc;
您没有提供太多的示例数据。如果在一个“子集”中,所有卡号的形式相同(数字部分没有前导零)。并支持REGEXP\u REPLACE
。使用它,您可以定义自定义函数:
DROP FUNCTION IF EXISTS zerofill_numbers;
CREATE FUNCTION zerofill_numbers(str TEXT, len TINYINT)
RETURNS text
NO SQL
DETERMINISTIC
RETURN REGEXP_REPLACE(
REGEXP_REPLACE(str, '(\\d+)', LPAD('\\1', len+2, 0)),
REPLACE('0*(\\d{$len})', '$len', len),
'\\1'
);
现在给出以下测试数据:
DROP TABLE IF EXISTS `strings`;
CREATE TABLE IF NOT EXISTS `strings` (`str` text);
INSERT INTO `strings` (`str`) VALUES
('Bowman 52'),
('Bowman 54'),
('Bowman 147'),
('Bowman Chrome Prospects BCP32'),
('Bowman Chrome Prospects BCP61'),
('Bowman Chrome Prospects BCP97'),
('Bowman Chrome Prospects BCP125'),
('Topps 1'),
('Topps 3'),
('Topps 2a'),
('Topps 2b'),
('v9.9.3'),
('v9.10.3'),
('v11.3.4'),
('v9.9.11'),
('v11.3'),
('0.9'),
('0.11'),
('s09'),
('s11'),
('s0'),
('v9.0.1');
我们可以将其分类为:
SELECT s.str
FROM strings s
ORDER BY zerofill_numbers(s.str, 10)
结果如下:
0.9
0.11
Bowman 52
Bowman 54
Bowman 147
Bowman Chrome Prospects BCP32
Bowman Chrome Prospects BCP61
Bowman Chrome Prospects BCP97
Bowman Chrome Prospects BCP125
s0
s09
s11
Topps 1
Topps 2a
Topps 2b
Topps 3
v9.0.1
v9.9.3
v9.9.11
v9.10.3
v11.3
v11.3.4
函数将用零填充字符串中的任何数字,直到其具有定义的长度
注1:这将无法正确排序十进制数字(请参见0.9
和0.11
)。您也不应该尝试将其用于有符号的数字
注2:此函数基于以下答案:-因此,如果您觉得此答案有用,请转到源代码并向上投票
注意3:如果不想定义自定义函数,可以使用相同的内联方法:
SELECT *
FROM strings
ORDER BY
REGEXP_REPLACE(REGEXP_REPLACE(str, '(\\d+)', LPAD('\\1', 10+2, 0)), '0*(\\d{10})', '\\1')
对不起,我将用一些附加信息进行编辑。尝试平衡保持数据的干净性,因为对于不习惯运动卡的人来说,数据可能会令人困惑,同时提供足够的数据以获得可靠的答案length()函数到底做了什么?只是
SELECT *
FROM strings
ORDER BY
REGEXP_REPLACE(REGEXP_REPLACE(str, '(\\d+)', LPAD('\\1', 10+2, 0)), '0*(\\d{10})', '\\1')