बहुत सारे अन्य उत्तर जो मैं यहां (और डुप्लिकेट प्रश्नों में) देख रहा हूं, मूल रूप से केवल बहुत विशेष रूप से स्वरूपित डेटा के लिए काम करते हैं , उदाहरण के लिए एक स्ट्रिंग जो पूरी तरह से एक संख्या है, या जिसके लिए एक निश्चित-लंबाई वाला अल्फाबेटिक उपसर्ग है। यह सामान्य मामले में काम नहीं कर रहा है।
यह सच है कि MySQL में 100% सामान्य नेट-सॉर्ट को लागू करने का वास्तव में कोई तरीका नहीं है, क्योंकि यह करने के लिए कि आप वास्तव में क्या संशोधित है तुलनात्मक फ़ंक्शन की , जो कि जब / जब टकराव होता है, तो स्ट्रिंग्स और संख्यात्मक प्रकार के लेक्सोग्राफ़िक सॉर्टिंग के बीच स्विच होता है एक संख्या। ऐसा कोड किसी भी एल्गोरिथ्म को लागू कर सकता है जिसे आप दो भागों के भीतर संख्यात्मक भागों को पहचानने और तुलना करने की इच्छा कर सकते हैं। दुर्भाग्य से, हालांकि, MySQL में तुलनात्मक फ़ंक्शन इसके कोड के लिए आंतरिक है, और उपयोगकर्ता द्वारा परिवर्तित नहीं किया जा सकता है।
यह किसी प्रकार का एक हैक छोड़ता है, जहाँ आप अपने स्ट्रिंग के लिए एक सॉर्ट कुंजी बनाने का प्रयास करते हैं जिसमें संख्यात्मक भाग फिर से स्वरूपित होते हैं ताकि मानक लेक्सिकोग्राफिक प्रकार वास्तव में उन्हें आपके इच्छित तरीके से सॉर्ट करें ।
सादे पूर्णांकों के लिए कुछ अधिकतम अंकों तक, स्पष्ट समाधान यह है कि उन्हें केवल शून्य पर रखें ताकि वे निश्चित चौड़ाई वाले हों। यह Drupal plugin और @plalx / @RichardToth के समाधान द्वारा लिया गया दृष्टिकोण है। (@ क्रिश्चियन के पास एक अलग और बहुत अधिक जटिल समाधान है, लेकिन यह कोई लाभ नहीं प्रदान करता है जो मैं देख सकता हूं)।
जैसा कि @tye बताते हैं, आप इस पर प्रत्येक अंक के लिए एक निश्चित-अंक की लंबाई बढ़ाकर सुधार कर सकते हैं, बजाए इसे केवल बाएं-पैड के। वहाँ बहुत है, बहुत अधिक आप पर सुधार कर सकते हैं, हालांकि, यहां तक कि क्या अनिवार्य रूप से एक अजीब हैक है की सीमाओं को देखते हुए। फिर भी, वहाँ कोई पूर्व निर्मित समाधान नहीं लगता है!
उदाहरण के लिए, किस बारे में:
- प्लस और माइनस संकेत? +10 बनाम 10 बनाम -10
- दशमलव? 8.2, 8.5, 1.006, .75
- अग्रणी शून्य? 020, 030, 00000922
- हजार विभाजक? "1,001 Dalmations" बनाम "1001 Dalmations"
- संस्करण संख्या? MariaDB v10.3.18 बनाम MariaDB v10.3.3
- बहुत लंबी संख्या? 103,768,276,592,092,364,859,236,487,687,870,234,598.55
@ Tye की विधि पर विस्तार करते हुए, मैंने एक काफी कॉम्पैक्ट NatSortKey () संग्रहित फ़ंक्शन बनाया है जो एक मनमाना स्ट्रिंग को एक नेट-सॉर्ट कुंजी में बदल देगा, और जो उपरोक्त सभी मामलों को संभालता है, यथोचित रूप से कुशल है, और कुल सॉर्ट को संरक्षित करता है- आदेश (कोई दो अलग-अलग स्ट्रिंग्स में सॉर्ट कीज़ नहीं हैं जो बराबर हैं)। एक दूसरे पैरामीटर का उपयोग प्रत्येक स्ट्रिंग में संसाधित संख्याओं की संख्या को सीमित करने के लिए किया जा सकता है (जैसे पहले 10 नंबर, कहते हैं), जिसका उपयोग किसी निश्चित लंबाई के भीतर आउटपुट फिट करने के लिए किया जा सकता है।
नोट: इस 2 पैरामीटर के दिए गए मान के साथ उत्पन्न सॉर्ट-की स्ट्रिंग को केवल पैरामीटर के लिए समान मान के साथ उत्पन्न अन्य स्ट्रिंग्स के खिलाफ सॉर्ट किया जाना चाहिए , अन्यथा वे सही तरीके से सॉर्ट नहीं कर सकते हैं!
आप इसे सीधे क्रम में उपयोग कर सकते हैं, जैसे
SELECT myString FROM myTable ORDER BY NatSortKey(myString,0);
लेकिन बड़ी तालिकाओं की कुशल छंटाई के लिए, दूसरे कॉलम में सॉर्ट कुंजी को प्री-स्टोर करना बेहतर है (संभवतः उस पर एक इंडेक्स के साथ):
INSERT INTO myTable (myString,myStringNSK) VALUES (@theStringValue,NatSortKey(@theStringValue,10)), ...
...
SELECT myString FROM myTable ORDER BY myStringNSK;
[आदर्श रूप से, आप कुंजी कॉलम को एक संगृहीत संग्रहीत कॉलम के रूप में बनाकर स्वचालित रूप से ऐसा करेंगे, जैसे कुछ:
CREATE TABLE myTable (
...
myString varchar(100),
myStringNSK varchar(150) AS (NatSortKey(myString,10)) STORED,
...
KEY (myStringNSK),
...);
लेकिन अभी तक न तो MySQL और न ही MariaDB गणना किए गए कॉलम में संग्रहीत कार्यों की अनुमति देता है , इसलिए दुर्भाग्य से आप अभी तक ऐसा नहीं कर सकते हैं ।]
मेरा कार्य केवल संख्याओं की छंटाई को प्रभावित करता है । यदि आप अन्य प्रकार के सामान्यीकरण कार्य करना चाहते हैं, जैसे कि सभी विराम चिह्न को हटाना, या प्रत्येक छोर से व्हाट्सएप को ट्रिम करना, या सिंगल-स्पेस के साथ मल्टी-व्हाट्सएप अनुक्रमों को बदलना, आप या तो फ़ंक्शन का विस्तार कर सकते हैं, या यह पहले या बाद NatSortKey()
में किया जा सकता है। आपके डेटा पर लागू होता है। (मैं REGEXP_REPLACE()
इस उद्देश्य के लिए उपयोग करने की सलाह दूंगा)।
यह कुछ हद तक एंग्लो-केंद्रित भी है कि मैं मान लेता हूं। ' एक दशमलव बिंदु के लिए और ',' हजारों-विभाजक के लिए, लेकिन यह संशोधित करने के लिए पर्याप्त होना चाहिए कि क्या आप रिवर्स चाहते हैं, या यदि आप चाहते हैं कि पैरामीटर के रूप में switchable हो।
यह अन्य तरीकों से और सुधार के लिए उत्तरदायी हो सकता है; उदाहरण के लिए यह वर्तमान में निरपेक्ष मान से ऋणात्मक संख्याओं को क्रमबद्ध करता है, इसलिए -1 -2 से पहले आता है, बजाय इसके कि अन्य तरीके से। पाठ के लिए ASC lexicographic क्रम को बनाए रखते हुए संख्याओं के लिए DESC सॉर्ट क्रम निर्दिष्ट करने का कोई तरीका नहीं है। इन दोनों मुद्दों को थोड़ा और काम के साथ तय किया जा सकता है; अगर मुझे समय मिलता है तो मैं कोड को अपडेट कर दूंगा।
इसके बारे में पता करने के लिए बहुत सारे अन्य विवरण हैं - चेस और कोलाजेशन पर कुछ महत्वपूर्ण निर्भरताओं सहित जो आप उपयोग कर रहे हैं - लेकिन मैंने उन सभी को SQL कोड के भीतर एक टिप्पणी ब्लॉक में डाल दिया है। अपने लिए फ़ंक्शन का उपयोग करने से पहले कृपया इसे ध्यान से पढ़ें!
तो, यहाँ कोड है। यदि आपको कोई बग मिल गया है, या एक सुधार है जिसका मैंने उल्लेख नहीं किया है, तो कृपया मुझे टिप्पणियों में बताएं!
delimiter $$
CREATE DEFINER=CURRENT_USER FUNCTION NatSortKey (s varchar(100), n int) RETURNS varchar(350) DETERMINISTIC
BEGIN
DECLARE x,y varchar(100);
DECLARE r varchar(350) DEFAULT '';
DECLARE suf varchar(101);
DECLARE i,j,k int UNSIGNED;
IF n<=0 THEN SET n := -1; END IF;
LOOP
SET i := REGEXP_INSTR(s,'\\d');
IF i=0 OR n=0 THEN RETURN CONCAT(r,s); END IF;
SET n := n-1, suf := ' ';
IF i>1 THEN
IF SUBSTRING(s,i-1,1)='.' AND (i=2 OR SUBSTRING(s,i-2,1) RLIKE '[^.\\p{L}\\p{N}\\p{M}\\x{608}\\x{200C}\\x{200D}\\x{2100}-\\x{214F}\\x{24B6}-\\x{24E9}\\x{1F130}-\\x{1F149}\\x{1F150}-\\x{1F169}\\x{1F170}-\\x{1F189}]') AND (SUBSTRING(s,i) NOT RLIKE '^\\d++\\.\\d') THEN SET i:=i-1; END IF;
IF i>1 AND SUBSTRING(s,i-1,1)='+' THEN SET suf := '+', j := i-1; ELSE SET j := i; END IF;
SET r := CONCAT(r,SUBSTRING(s,1,j-1)); SET s = SUBSTRING(s,i);
END IF;
SET x := REGEXP_SUBSTR(s,IF(SUBSTRING(s,1,1) IN ('0','.') OR (SUBSTRING(r,-1)=',' AND suf=' '),'^\\d*+(?:\\.\\d++)*','^(?:[1-9]\\d{0,2}(?:,\\d{3}(?!\\d))++|\\d++)(?:\\.\\d++)*+'));
SET s := SUBSTRING(s,LENGTH(x)+1);
SET i := INSTR(x,'.');
IF i=0 THEN SET y := ''; ELSE SET y := SUBSTRING(x,i); SET x := SUBSTRING(x,1,i-1); END IF;
SET i := LENGTH(x);
SET x := REPLACE(x,',','');
SET j := LENGTH(x);
SET x := TRIM(LEADING '0' FROM x);
SET k := LENGTH(x);
SET suf := CONCAT(suf,LPAD(CONV(LEAST((j-k)*2,1294) + IF(i=j,0,1),10,36),2,'0'));
SET i := LOCATE('.',y,2);
IF i=0 THEN
SET r := CONCAT(r,LPAD(CONV(LEAST(k,359),10,36),2,'0'),x,y,suf);
ELSE
SET r := CONCAT(r,LPAD(CONV(LEAST(k,359),10,36),2,'0'),x);
WHILE LENGTH(y)>0 AND n!=0 DO
IF i=0 THEN SET x := SUBSTRING(y,2); SET y := ''; ELSE SET x := SUBSTRING(y,2,i-2); SET y := SUBSTRING(y,i); SET i := LOCATE('.',y,2); END IF;
SET j := LENGTH(x);
SET x := TRIM(LEADING '0' FROM x);
SET k := LENGTH(x);
SET r := CONCAT(r,LPAD(CONV(LEAST(k,359),10,36),2,'0'),x);
SET suf := CONCAT(suf,LPAD(CONV(LEAST((j-k)*2,1294),10,36),2,'0'));
SET n := n-1;
END WHILE;
SET r := CONCAT(r,y,suf);
END IF;
END LOOP;
END
$$
delimiter ;