簡體   English   中英

如何在mysql中對字母數字數據進行排序?

[英]How to sort alphanumeric data in mysql?

首先,我想指出我幾乎嘗試過所有事情。 我從最近8個小時開始嘗試按順序列出我的列表,並且我已經應用了這里找到的十幾個解決方案。

這是SQL Fiddle的示例數據。 我找到了一個能夠按正確的順序對列表進行排序的頁面 ,即:

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 - lpad_numbers字符串中的每個數字。 這有點難看,因為我不太了解MySQL的功能,但嘿,它的工作原理非常快。

例:

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;

根據底層結構分裂

從技術上講,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它們進行排序ORDER BY alpha1, num1, alpha2, num2它們將按您的需要進行排序。 但是已經“格式化”的版本( Original列)無法輕易排序,因為應按字母順序排序的部分和應按數字排序的部分混合在一起。

zeropadding

有一個稍微不那么廣泛的替代方案,只需要一個額外的列,你假設沒有任何數字超過讓我們說10000並且你現在可以用零填充版本替換每個數字(不是數字!),所以10 A-1將成為0010A0001 (顯然是0010A0001 ),但我不認為這是在ORDER BY語句中即時進行的。

但是對於這個例子,zeropadded版本(假設:每個數字<10000):

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      

這可以通過ORDER BY zeropadded對您的願望進行排序。

因此,最后,您可能需要在php中排序或創建更多列,以幫助您通過重新格式化/清理/拆分輸入進行排序。

更新

zeropadding解釋(簡化)

zeropadding背后的主要思想是數字的自然格式與計算機中的格式不同。 在計算機中, 數字 2實際上是數字序列 0..0002(因此包括前導零)類似10(0..0010)。 當計算機比較數字時,它將從左到右,直到找到不同的數字:

0...0002
0...0010
======!.    (the ! marks the point where the first digit is different)

然后它將確定哪個數字更大或更小。 在這種情況下,0 <1,因此2 <10.(當然,計算機使用二進制,但這並沒有改變想法)。

現在,字符串在技術上是一系列字符 字符串比較工作略有不同。 當比較兩個字符串時,它們不會(左)填充,因此每個字符串的第一個字符實際上是第一個字符而不是填充(例如空格)。 因此從技術上講, 字符串 A10是字符序列A10 並且由於使用了字符串比較,它比A2 “小”,因為字符串比較不會將數字看作數字而是作為字符(即數字):

A10
A2
=!     (the ! marks the point where the first character is different)

因為1 < 2為字符, A10 < A2 現在為了避免這個問題,我們強制字符串中數字的格式與數字比較中的數字格式相同,方法是將數字填充到相同的長度,根據它們的位置值對齊數字:

A0010
A0002
===!.  (the ! marks the point where the first character is different)

現在,它實際上與您在數值比較中所期望的相同。 但是,您必須對數字的最大長度做出一些假設,以便您可以適當地選擇填充。 沒有這個假設,你就會遇到問題。

剩下的唯一(邏輯)點:當比較的字符串具有字母字符而另一個具有數字時,填充會發生什么變化? 答案是:沒什么。 我們不會將數字更改為字母,並且數字小於字母,因此在這種情況下所有內容都保持相同的順序。

zeropadding的效果是:我們通過根據數字字符對齊數字字符,將字符串中的“數字”比較調整為與實數比較相似。

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解決方案更加棘手和復雜。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM