当列可以以字母开头或结尾时,使用MySQL对varchar列进行数字排序并强制转换为无符号

当列可以以字母开头或结尾时,使用MySQL对varchar列进行数字排序并强制转换为无符号,mysql,sql,sorting,natural-sort,Mysql,Sql,Sorting,Natural Sort,我遇到了一些事情,我真的不知道该怎么处理。我正在建立一个数据库来存储运动卡上的信息,当我想查看某些卡时,我在排序方面遇到了一些问题 作为背景,每张卡(数据库中的一行)都有关于年份、设置卡的来源、卡上的玩家和卡号的信息(还有更多的信息,但这就是所有相关信息)。当我看到结果时,我希望事情按年份排序,然后是集合,然后是玩家,然后是卡号。除了卡号之外,其他一切都很正常,因为年份只是一个整数,而且set和player都是varchar,所以很容易对它们进行排序。然而,卡号正是我遇到的一些问题 卡号列是va

我遇到了一些事情,我真的不知道该怎么处理。我正在建立一个数据库来存储运动卡上的信息,当我想查看某些卡时,我在排序方面遇到了一些问题

作为背景,每张卡(数据库中的一行)都有关于年份、设置卡的来源、卡上的玩家和卡号的信息(还有更多的信息,但这就是所有相关信息)。当我看到结果时,我希望事情按年份排序,然后是集合,然后是玩家,然后是卡号。除了卡号之外,其他一切都很正常,因为年份只是一个整数,而且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字符串排序部分的方式:

order by year desc, cardset asc, subset asc, cast(cardNum as unsigned) asc;
这是处理大多数事情的好方法。但我遇到的问题是,当一组卡片的卡号中有相同的字母,然后有一个数字。我希望排序基本上忽略前导字母,然后只按数字排序。但是,有时它不能正确地执行此操作,特别是当有超过5张的卡片需要排序时

具体而言,它错误地将一些具有以下卡号的卡按以下顺序排序:

  • BCP61
  • BCP97
  • BCP32
  • BCP135
当其应导致:

  • BCP32
  • BCP61
  • BCP97
  • BCP135
它目前正在对直接数字进行排序,或者正确地将数字后跟字母(即1、2、3、4、5或1、2、3a、3b、4、5a、5b)。我不知道它对直连字母的排序有什么问题,但我目前也很少有这样的测试用例,所以我不确定它是否是100%

除了不知道如何在不破坏其他排序的情况下修改SQL sort语句外,我还不知道它是如何按照上面的BCP示例的顺序进行的。有没有关于如何纠正的想法?我曾想过,在我们找到数字之前,尽量忽略卡号中的字母,但这会导致卡号中只有字母的卡出现重大问题。所以我有点困了

绝对最坏到最坏,我可能会把卡号列分成两个不同的列,一个用于更具描述性的部分,另一个用于我想要排序的部分。这可能会很好地结束工作,但将需要大量的工作,以得到东西回来

编辑-这里有一些更多信息,包括my DB中的数据(很抱歉格式化,不知道如何在这里处理表格):

我希望我这一类人能按以下顺序给出结果:

  • 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年排名第三
它可以处理只有数字的卡号,或者数字后面跟着字母,但当卡号以字母开头,后面跟着数字时,它往往会把事情搞砸

我尝试在注释中使用length()技巧,因此SQL的排序部分是:

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