如何在mysql中对字母数字数据进行排序?

时间:2016-08-18 15:29:10

标签: php mysql sorting

首先,我想指出我几乎尝试过所有事情。我从最近8个小时开始尝试按顺序列出我的列表,并且我已经应用了这里找到的十几个解决方案。

以下是SQL Fiddle的示例数据。我发现a page能够按正确的顺序对我的列表进行排序,即:

1
2
2.B3
5
9
10 A-1
10 A-3
10 B-4
10 B-5
11
12
B3-43
B3-44
B3 - 48
B3 - 49
Basztowa 3
Basztowa 4
Basztowa 5
Basztowa 7
Basztowa 9
D.1
D.2
D.10
D.11
D.12
Kabaty ul. Pod lipą 4

但我无法使用MySQL重现这一点。

我会感激任何帮助,因为我没有更多的想法。我考虑使用PHP来对我的列表进行排序,但据我所知,DBMS针对这个操作孩子进行了优化,所以如果可能的话我想避免使用PHP来实现这一点。

@UPDATE

感谢@Jakumi我创建了两个帮助我解决问题的功能。

您需要创建一个列,以便以排序友好格式存储您的值( zeropadded_name ),在更新时创建触发器,并在名称时插入以填充 zeropadded_name 改变就是这样!现在只需通过 zeropadded_name 订购并享受!

帮助程序功能

  1. regex_replace - 它的任务是通过删除所有非字母数字字符来帮助我们消毒价值。
  2. lpad_numbers - 填充字符串中的每个数字。这有点难看,因为我不太了解MySQL的功能,但嘿,它的工作原理非常快。
  3. 示例:

    SELECT lpad_numbers(regex_replace('[^a-zA-Z0-9]', ' ', 'B3 - A-5'));
    #B0003A0005
    
    DROP FUNCTION IF EXISTS regex_replace;
    CREATE FUNCTION `regex_replace`(
      pattern     VARCHAR(1000)
                  CHARSET utf8
                  COLLATE utf8_polish_ci,
      replacement VARCHAR(1000)
                  CHARSET utf8
                  COLLATE utf8_polish_ci,
      original    VARCHAR(1000)
                  CHARSET utf8
                  COLLATE utf8_polish_ci
    ) RETURNS varchar(1000) CHARSET utf8
        DETERMINISTIC
    BEGIN
        DECLARE temp VARCHAR(1000)
        CHARSET utf8
        COLLATE utf8_polish_ci;
        DECLARE ch VARCHAR(1)
        CHARSET utf8
        COLLATE utf8_polish_ci;
        DECLARE i INT;
        SET i = 1;
        SET temp = '';
        IF original REGEXP pattern
        THEN
          loop_label: LOOP
            IF i > CHAR_LENGTH(original)
            THEN
              LEAVE loop_label;
            END IF;
            SET ch = SUBSTRING(original, i, 1);
            IF NOT ch REGEXP pattern
            THEN
              SET temp = CONCAT(temp, ch);
            ELSE
              SET temp = CONCAT(temp, replacement);
            END IF;
            SET i = i + 1;
          END LOOP;
        ELSE
          SET temp = original;
        END IF;
        RETURN temp;
      END;
    
    DROP FUNCTION IF EXISTS lpad_numbers;
    CREATE FUNCTION `lpad_numbers`(str VARCHAR(256)) RETURNS varchar(256) CHARSET utf8 COLLATE utf8_polish_ci
    BEGIN
        DECLARE i, len SMALLINT DEFAULT 1;
        DECLARE ret VARCHAR(256) DEFAULT '';
        DECLARE num VARCHAR(256) DEFAULT '';
        DECLARE c CHAR(1);
    
        IF str IS NULL
        THEN
          RETURN "";
        END IF;
    
        SET len = CHAR_LENGTH(str);
        REPEAT
          BEGIN
            SET c = MID(str, i, 1);
            IF c BETWEEN '0' AND '9'
            THEN
              SET num = c;
              SET i = i + 1;
              REPEAT
                BEGIN
                  SET c = MID(str, i, 1);
                  SET num = CONCAT(num, c);
                  SET i = i + 1;
                END;
              UNTIL c NOT BETWEEN '0' AND '9' END REPEAT;
              SET ret = CONCAT(ret, LPAD(num, 4, '0'));
            ELSE
              SET ret = CONCAT(ret, c);
              SET i = i + 1;
            END IF;
          END;
        UNTIL i > len END REPEAT;
        RETURN ret;
      END;
    

2 个答案:

答案 0 :(得分:2)

根据基础结构分裂

从技术上讲,mysql排序机制可以正常工作,但是你的字符串是以错误的方式格式化的。您的数据的基础结构类似于以下内容(为了便于与示例关联而保留Original列):

alpha1   num1 alpha2 num2 ...   Original      
            1                   1             
            2                   2             
            2      B    3       2.B3          
            5                   5             
            9                   9             
           10      A    1       10 A-1        
           10      A    3       10 A-3        
           10      B    4       10 B-4        
           10      B    5       10 B-5        
           11                   11            
           12                   12            
B           3          43       B3-43         
B           3          44       B3-44         
B           3          48       B3 - 48       
B           3          49       B3 - 49       
Basztowa    3                   Basztowa 3    
Basztowa    4                   Basztowa 4    
Basztowa    5                   Basztowa 5    
Basztowa    7                   Basztowa 7    
Basztowa    9                   Basztowa 9    
D           1                   D.1           
D           2                   D.2           
D          10                   D.10          
D          11                   D.11          
D          12                   D.12          

如果您现在使用ORDER BY alpha1, num1, alpha2, num2对它们进行排序,它们将按您的需要进行排序。但是已经“格式化”的版本(Original列)无法轻易排序,因为应按字母顺序排序的部分和应按数字排序的部分混合在一起。

zeropadding

有一个不那么广泛的替代方案只需要一个额外的列,你假设没有任何数字超过让我们说10000并且你现在可以用零填充版本替换每个数字(不是数字!),所以{{1} } 10 A-1(显然是0010A00010010以及A,但我不认为这是在{{{} { 1}}陈述。

但是对于这个例子,zeropadded版本(假设:每个数字<10000):

0001

这可以通过ORDER BY对您的愿望进行排序。

所以最后,您可能需要在php中排序或创建更多列,以帮助您通过重新格式化/清理/拆分输入进行排序。

<强>更新

zeropadding解释(简化)

zeropadding背后的主要思想是数字的自然格式与计算机中的格式不同。在计算机中,数字 2实际上是数字序列 0..0002(所以包括前导零)类似10(0..0010)。当计算机比较数字时,它将从左到右,直到找到不同的数字:

Original      Zeropadded 
1             0001       
2             0002       
2.B3          0002B0003  
5             0005       
9             0009       
10 A-1        0010A0001  
10 A-3        0010A0003  
10 B-4        0010B0004  
10 B-5        0010B0005  
11            0011       
12            0012       
B3-43         B00030043  
B3-44         B00030043  
B3 - 48       B00030048  
B3 - 49       B00030049  
Basztowa 3    Baztowa0003
Basztowa 4    Baztowa0004
Basztowa 5    Baztowa0005
Basztowa 7    Baztowa0007
Basztowa 9    Baztowa0009
D.1           D0001      
D.2           D0002      
D.10          D0010      
D.11          D0011      
D.12          D0012      

然后它将确定哪个数字更大或更小。在这种情况下,0 < 1,因此2&lt; 10.(当然计算机使用二进制,但这不会改变这个想法。)

现在,字符串在技术上是字符序列。字符串比较工作略有不同。当比较两个字符串时,它们不会(左)填充,因此每个字符串的第一个字符实际上是第一个字符而不是填充(例如空格)。从技术上讲,字符串 ORDER BY zeropadded是一系列字符0...0002 0...0010 ======!. (the ! marks the point where the first digit is different) A10A。并且由于使用了字符串比较,它比1“更小”,因为字符串比较不会将数字看作数字而是作为字符(即数字):

0

因为A2&lt; A10 A2 =! (the ! marks the point where the first character is different) 为字符,1&lt; 2。现在为了避免这个问题,我们强制字符串中的数字格式与数字比较中的数字格式相同,方法是将数字填充到相同的长度,根据place value对齐数字:< / p>

A10

现在,它实际上与您在数值比较中所期望的相同。但是,您必须对数字的最大长度做出一些假设,以便您可以适当地选择填充。没有这个假设,你就会遇到问题。

剩下的唯一(逻辑)点:当比较的字符串具有字母字符,而另一个字符串具有数字时,填充变化是什么?答案是:没什么。我们不会将数字更改为字母,并且数字小于字母,因此在这种情况下所有内容都保持相同的顺序。

zeropadding的效果是:我们通过根据数字字符对齐数字字符来调整字符串中的“数字”比较,使其与实数比较相似。

答案 1 :(得分:-2)

SELECT name FROM realestate ORDER BY name ASC;

这应该用字母数字数据对您的列表进行排序......我没有看到问题。

编辑:好的,我还是不知道我是否真的明白这个问题的目标是什么(比赛是针对什么?),但是我可以提交这个“扭曲”的查询(我希望我永远不会使用)在我的职业生涯中):

SELECT name FROM realestate
ORDER BY IF(SUBSTRING(name, 1, 2) REGEXP '[A-Z]', 100000, CAST(name AS UNSIGNED)) ASC,
SUBSTRING(name, 1, 2) ASC,
CAST(SUBSTRING(name FROM LOCATE('.', name)+1) AS UNSIGNED) ASC,
REPLACE(name, ' ', '') ASC;

也许有人可以找到一种更简单的方法,因为我承认我的答案有点复杂。但是,Kamil和Jakumi解决方案更加棘手和复杂。