【问题标题】:How to sort string which contains numbers and alphabets?如何对包含数字和字母的字符串进行排序?
【发布时间】:2019-03-28 19:39:38
【问题描述】:

我有 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 值属于同一个字段和同一个表的不同行。

从这里开始我不知道该怎么做。

任何帮助将不胜感激。

编辑

这是play with 的示例数据库。

【问题讨论】:

  • gt_no 列有什么类型?
  • varchar(10),但我研究了一些,意识到,我需要自然排序顺序
  • 请创建一个 SQL Fiddle 或 DB Fiddle 并将其添加到问题中,供其他人使用。
  • 您可以使用此答案中创建的函数:stackoverflow.com/a/37269038/2469308 并从字符串中获取数字,从字符串中获取非数字部分。
  • 检查这个小提琴:db-fiddle.com/f/e5bEtYDzMqqvNRCs3qN6iD/1 它对你有用吗?

标签: mysql sorting mysql-5.7


【解决方案1】:

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

【讨论】:

  • 它显示错误。 REGEXP_REPLACE does not exist
  • 可能是旧数据库。 REGEXP_REPLACE 在 8.0 中可用...你能指定你正在运行的版本吗?编辑:我刚刚在另一条评论中看到,5.7
  • 是的。我的服务器的 mysql 版本是 5.7,你至少可以给我查询以仅获取字母和仅作为两个不同字段的数字,我将对这两个字段进行排序。因为数据库无法创建两个单独的字段。希望你能理解
  • 我会实现你的编辑部分。但它仍然无法正常工作
  • 我只是在你的小提琴中运行它,没有错误:db-fiddle.com/f/e5bEtYDzMqqvNRCs3qN6iD/2
【解决方案2】:

由于 MySQL 版本

以下是此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

【讨论】:

    【解决方案3】:

    不漂亮,因为我们正在解析字符串,所以它会很慢。 这假设您的格式是 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
    

    【讨论】:

    • 是的,尝试了一个不同的变体,使用用户变量来显示发生了什么,真的可以嵌入每个公式
    【解决方案4】:

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

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

    【讨论】:

    • 对不起,伙计。但是不确定字母或字母的数量可能会出现在一个字符串中。我需要自然排序顺序
    • 那么@Amadan的回答是对的,你只需要用正则表达式取字母,然后按整数排序,现在在mysql中搜索正则表达式函数..呵呵
    • 服务器的mysql 5.7版本不可用
    猜你喜欢
    • 2016-10-28
    • 2022-11-23
    • 2021-02-22
    • 1970-01-01
    • 2021-09-19
    • 1970-01-01
    • 2021-08-12
    相关资源
    最近更新 更多