如何对包含数字和字母的字符串进行排序?

How to sort string which contains numbers and alphabets?

我有 qt_no 个值,例如

AM1,M3,M4,M14,M30,M40,MA01,A10,A13,A07,B01,B10,Z33,Z13

等(实际上是字母后的任何 int 2-3 位数字)。

我试过按

排序
order by length(qt_no), qt_no

它没有达到我要求的输出。

我的预期输出是

A01,A07,A10,A13,B01,AM1,M3,M4,M14,M30,M40,MA01,Z13,Z33

请记住,这些 qt_no 值属于同一字段,但属于同一 table 的不同行。

我不知道接下来该做什么。

任何帮助将不胜感激。

编辑

这是 play with 的示例数据库。

最好的方案是创建两个额外的列,一个用于字母部分,一个用于数字部分;那么它就像ORDER BY alpha_part ASC, num_part ASC一样简单。如果你在这两列上有一个联合索引,它也会非常快。

如果您绝对必须在查询时解析该列,那会花费时间 - 并且还会使索引无用,这会使一切变得更慢。但是你可以这样做:

...
ORDER BY
  REGEXP_REPLACE(qt_no, '\d+', '') ASC, 
  CAST(REGEXP_REPLACE(qt_no, '\D+', '') AS INTEGER) ASC

编辑:非常抱歉,但我不知道如何在 5.7 上执行此操作,除非像这样:

SELECT qt_no FROM t
ORDER BY
REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(qt_no, '0', ''), '1', ''), '2', ''), '3', ''), '4', ''), '5', ''), '6', ''), '7', ''), '8', ''), '9', '') ASC, 
CAST(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(qt_no, 'A', ''), 'B', ''), 'C', ''), 'D', ''), 'E', ''), 'F', ''), 'G', ''), 'H', ''), 'I', ''), 'J', ''), 'K', ''), 'L', ''), 'M', ''), 'N', ''), 'O', ''), 'P', ''), 'Q', ''), 'R', ''), 'S', ''), 'T', ''), 'U', ''), 'V', ''), 'W', ''), 'X', ''), 'Y', ''), 'Z', '') AS UNSIGNED) ASC;

您可以将顺序分为三个阶段:首先是字母,然后是字符串的长度,最后是字母顺序:

select * from test order by substring( qt_no, 1, 1 ), length(qt_no), qt_no;

不漂亮,因为我们正在解析字符串,所以已经说过,它会很慢。 这假设您的格式是 Alpha,然后数值永远不会混合。它找到第一个数值,然后根据该发现分成两列。

DEMO:

我只是想按 qt_no+0 排序以获得自然排序,但这没有用。 所以我走了一条不同的路....

字段 1 是您的 qt_no 字段...

SELECT Field1,

#Use this to just get the number values but since we lose trailing zeros...Step 2 we reverse the value so numbers are first allowing the convert to drop the letters.  unfortunately this also drops the trailing (leading since we reversed) zeros.
       @NumStep1 := reverse(CONVERT(reverse(Field1), SIGNED)) NumStep1,

#We got the postiion of the first number... so get the  whole number now.
       @NumStep2 :=substring(Field1,locate(@numStep1,Field1),length(Field1)) NumStep2,
       @Alpha:= substring(Field1,1,Locate(@numStep2,Field1)-1) Alpha

FROM (
SELECT 'AM1' as Field1 UNION ALL 
SELECT 'M3' as Field1 UNION ALL
SELECT 'M4' as Field1 UNION ALL
SELECT 'M14' as Field1 UNION ALL
SELECT 'M30' as Field1 UNION ALL
SELECT 'M40' as Field1 UNION ALL
SELECT 'MA01' as Field1 UNION ALL
SELECT 'A10' as Field1 UNION ALL
SELECT 'A13' as Field1 UNION ALL
SELECT 'A07' as Field1 UNION ALL
SELECT 'B01' as Field1 UNION ALL
SELECT 'B10' as Field1 UNION ALL
SELECT 'Z33' as Field1 UNION ALL
SELECT 'Z13' as Field1) Z
ORDER BY Alpha, NumStep2*1

给我们:

+----+--------+----------+----------+-------+
|    | Field1 | NumStep1 | NumStep2 | Alpha |
+----+--------+----------+----------+-------+
|  1 | A10    |        1 |       10 | A     |
|  2 | A13    |       13 |       13 | A     |
|  3 | A07    |       07 |       07 | A     |
|  4 | AM1    |        1 |        1 | AM    |
|  5 | B10    |        1 |       10 | B     |
|  6 | B01    |       01 |       01 | B     |
|  7 | M3     |        3 |        3 | M     |
|  8 | M4     |        4 |        4 | M     |
|  9 | M14    |       14 |       14 | M     |
| 10 | M30    |        3 |       30 | M     |
| 11 | M40    |        4 |       40 | M     |
| 12 | MA01   |       01 |       01 | MA    |
| 13 | Z33    |       33 |       33 | Z     |
| 14 | Z13    |       13 |       13 | Z     |
+----+--------+----------+----------+-------+

没有用户变量,但将数据拆分为字母和数字。

SELECT Field1,
       substring(Field1,locate(reverse(CONVERT(reverse(Field1), SIGNED)),Field1),length(Field1)) NumStep2,
       substring(Field1,1,Locate(substring(Field1,locate(reverse(CONVERT(reverse(Field1), SIGNED)),Field1),length(Field1)),Field1)-1) Alpha

FROM (
SELECT 'AM1' as Field1 UNION ALL 
SELECT 'M3' as Field1 UNION ALL
SELECT 'M4' as Field1 UNION ALL
SELECT 'M14' as Field1 UNION ALL
SELECT 'M30' as Field1 UNION ALL
SELECT 'M40' as Field1 UNION ALL
SELECT 'MA01' as Field1 UNION ALL
SELECT 'A10' as Field1 UNION ALL
SELECT 'A13' as Field1 UNION ALL
SELECT 'A07' as Field1 UNION ALL
SELECT 'B01' as Field1 UNION ALL
SELECT 'B10' as Field1 UNION ALL
SELECT 'Z33' as Field1 UNION ALL
SELECT 'Z13' as Field1) Z
ORDER BY Alpha, NumStep2*1

由于 MySQL 版本 < 8.0 中缺少 Regex 函数,我们可以创建一个自定义函数来从给定字符串中提取数字子字符串。

以下是此 的修改函数,其中 returns 来自输入字符串的整数值。这里所做的修改是它 returns string 而不是 Int。因为你有像 07 这样的数字字符串,需要原样返回,而不是 7.

DELIMITER $$

CREATE FUNCTION `ExtractNumber`(in_string VARCHAR(50)) 
RETURNS VARCHAR(50)
NO SQL
BEGIN
    DECLARE ctrNumber VARCHAR(50);
    DECLARE finNumber VARCHAR(50) DEFAULT '';
    DECLARE sChar VARCHAR(1);
    DECLARE inti INTEGER DEFAULT 1;

    IF LENGTH(in_string) > 0 THEN
        WHILE(inti <= LENGTH(in_string)) DO
            SET sChar = SUBSTRING(in_string, inti, 1);
            SET ctrNumber = FIND_IN_SET(sChar, '0,1,2,3,4,5,6,7,8,9'); 
            IF ctrNumber > 0 THEN
                SET finNumber = CONCAT(finNumber, sChar);
            END IF;
            SET inti = inti + 1;
        END WHILE;
        RETURN finNumber;
    ELSE
        RETURN '';
    END IF;    
END$$

DELIMITER ;

现在,您可以使用此自定义函数,按字母部分排序,然后按数字部分排序(转换为 unsigned)。

SELECT id, 
       name,
       REPLACE(name, ExtractNumber(name), '') as strpart, 
       CAST(ExtractNumber(name) AS UNSIGNED) as numpart 
FROM test
ORDER BY strpart, 
         numpart

DB Fiddle DEMO