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

时间:2018-10-24 11:43:52

标签: mysql sorting mysql-5.7

我有qt_no值,例如

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

etc(实际上是一个字母后的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值是同一表的相同字段和不同行。

我不知道从现在开始该怎么做。

任何帮助将不胜感激。

编辑

这是play with的示例数据库。

4 个答案:

答案 0 :(得分:4)

最佳方案是创建两个额外的列,一列用于字母部分,一列用于数字部分;那么它就像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;

答案 1 :(得分:3)

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

以下是对此answer的修改后的函数,该函数从输入字符串返回整数值。此处所做的修改是它返回字符串而不是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

答案 2 :(得分:2)

它不漂亮,因为我们正在字符串解析,所以已经很慢了。 假设您的格式为Alpha,则永远不要混合数值。它找到第一个数字值,然后根据该结果将其分为两列。

DEMO:

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

Field1是您的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

答案 3 :(得分:0)

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

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