[英]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訂購並享受!
助手功能
regex_replace
- 它的任務是通過刪除所有非字母數字字符來幫助我們清理值。 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
列)無法輕易排序,因為應按字母順序排序的部分和應按數字排序的部分混合在一起。
有一個稍微不那么廣泛的替代方案,只需要一個額外的列,你假設沒有任何數字超過讓我們說10000並且你現在可以用零填充版本替換每個數字(不是數字!),所以10 A-1
將成為0010A0001
(顯然是0010
和A
和0001
),但我不認為這是在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背后的主要思想是數字的自然格式與計算機中的格式不同。 在計算機中, 數字 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
是字符序列A
, 1
和0
。 並且由於使用了字符串比較,它比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.