 |
การเรียง sql order แบบตัวเลขพร้อมตัวอักษร (2021) |
|
 |
|
|
 |
 |
|
ตัวอย่างที่เรียกใช้แบบธรรมดา
Code (SQL)
SELECT * FROM `products` ORDER BY `product_name` ASC
แบบนี้ จะได้ผลลัพธ์ออกมาเป็น
หนังสือเล่ม 1 ...ไปจนถึง หนังสือเล่ม 19
หนังสือเล่ม 2 ...ไปจนถึง หนังสือเล่ม 29
วิธีแก้
วิธีที่ 1
ใช้ LENGTH เข้ามาช่วย
Code (SQL)
SELECT * FROM `products` ORDER BY LENGTH(`product_name`) ASC, `product_name` ASC
จะได้ผลลัพธ์ที่ถูกต้อง คือ
หนังสือเล่ม 1
หนังสือเล่ม 2
หนังสือเล่ม 3 ... ไปเรื่อยๆ
ทั้งนี้คำสั่ง LENGTH() จะนับจำนวนสตริงในคอลัมน์ที่ระบุ และจะมีผลลัพธ์ผิดพลาดทันทีถ้าหากคอลัมน์นั้นมีข้อมูลลักษณะนี้ เช่น Final Fantasy, Goofy. ตัวข้อมูล Goofy จะถูกนำขึ้นมาก่อน Final Fantasy เพราะจำนวนสตริงน้อยกว่า คำสั่งนี้จึงไม่เหมาะกับสถานการณ์ดังที่กล่าว.
วิธีที่ 2
สร้าง function, routine ขึ้นมาช่วย
Code (SQL)
/**
* Returns a string formatted for natural sorting. This function is very useful when having to sort alpha-numeric strings.
*
* @author Alexandre Potvin Latreille (plalx)
* @param {nvarchar(4000)} instring The formatted string.
* @param {int} numberLength The length each number should have (including padding). This should be the length of the longest number. Defaults to 10.
* @param {char(50)} sameOrderChars A list of characters that should have the same order. Ex: '.-/'. Defaults to empty string.
*
* @return {nvarchar(4000)} A string for natural sorting.
* Example of use:
*
* SELECT Name FROM TableA ORDER BY Name
* TableA (unordered) TableA (ordered)
* ------------ ------------
* ID Name ID Name
* 1. A1. 1. A1-1.
* 2. A1-1. 2. A1.
* 3. R1 --> 3. R1
* 4. R11 4. R11
* 5. R2 5. R2
*
*
* As we can see, humans would expect A1., A1-1., R1, R2, R11 but that's not how SQL is sorting it.
* We can use this function to fix this.
*
* SELECT Name FROM TableA ORDER BY dbo.udf_NaturalSortFormat(Name, default, '.-')
* TableA (unordered) TableA (ordered)
* ------------ ------------
* ID Name ID Name
* 1. A1. 1. A1.
* 2. A1-1. 2. A1-1.
* 3. R1 --> 3. R1
* 4. R11 4. R2
* 5. R2 5. R11
*/
DROP FUNCTION IF EXISTS `udf_FirstNumberPos`;
DELIMITER ;;
CREATE FUNCTION `udf_FirstNumberPos` (`instring` varchar(4000))
RETURNS int
LANGUAGE SQL
DETERMINISTIC
NO SQL
SQL SECURITY INVOKER
BEGIN
DECLARE position int;
DECLARE tmp_position int;
SET position = 5000;
SET tmp_position = LOCATE('0', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF;
SET tmp_position = LOCATE('1', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF;
SET tmp_position = LOCATE('2', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF;
SET tmp_position = LOCATE('3', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF;
SET tmp_position = LOCATE('4', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF;
SET tmp_position = LOCATE('5', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF;
SET tmp_position = LOCATE('6', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF;
SET tmp_position = LOCATE('7', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF;
SET tmp_position = LOCATE('8', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF;
SET tmp_position = LOCATE('9', instring); IF (tmp_position > 0 AND tmp_position < position) THEN SET position = tmp_position; END IF;
IF (position = 5000) THEN RETURN 0; END IF;
RETURN position;
END
;;
DROP FUNCTION IF EXISTS `udf_NaturalSortFormat`;
DELIMITER ;;
CREATE FUNCTION `udf_NaturalSortFormat` (`instring` varchar(4000), `numberLength` int, `sameOrderChars` char(50))
RETURNS varchar(4000)
LANGUAGE SQL
DETERMINISTIC
NO SQL
SQL SECURITY INVOKER
BEGIN
DECLARE sortString varchar(4000);
DECLARE numStartIndex int;
DECLARE numEndIndex int;
DECLARE padLength int;
DECLARE totalPadLength int;
DECLARE i int;
DECLARE sameOrderCharsLen int;
SET totalPadLength = 0;
SET instring = TRIM(instring);
SET sortString = instring;
SET numStartIndex = udf_FirstNumberPos(instring);
SET numEndIndex = 0;
SET i = 1;
SET sameOrderCharsLen = CHAR_LENGTH(sameOrderChars);
WHILE (i <= sameOrderCharsLen) DO
SET sortString = REPLACE(sortString, SUBSTRING(sameOrderChars, i, 1), ' ');
SET i = i + 1;
END WHILE;
WHILE (numStartIndex <> 0) DO
SET numStartIndex = numStartIndex + numEndIndex;
SET numEndIndex = numStartIndex;
WHILE (udf_FirstNumberPos(SUBSTRING(instring, numEndIndex, 1)) = 1) DO
SET numEndIndex = numEndIndex + 1;
END WHILE;
SET numEndIndex = numEndIndex - 1;
SET padLength = numberLength - (numEndIndex + 1 - numStartIndex);
IF padLength < 0 THEN
SET padLength = 0;
END IF;
SET sortString = INSERT(sortString, numStartIndex + totalPadLength, 0, REPEAT('0', padLength));
SET totalPadLength = totalPadLength + padLength;
SET numStartIndex = udf_FirstNumberPos(RIGHT(instring, CHAR_LENGTH(instring) - numEndIndex));
END WHILE;
RETURN sortString;
END
;;
โดยคำสั่งนี้จะเป็นการสร้าง function ขึ้นมา ให้เราสั่งเพียงครั้งเดียวแล้วฐานข้อมูลจะสร้าง function ไว้ใน routines ซึ่งการเรียกใช้ครั้งต่อไปก็เรียกใช้ได้เลย.
ตัวอย่างการเรียกใช้.
Code (SQL)
SELECT * FROM `products` ORDER BY udf_NaturalSortFormat(`product_name`, 10, ".") ASC
ที่มา: https://stackoverflow.com/a/12257917/128761
https://rundiz.com/?p=176
ข้อมูลตัวอย่างสำหรับทดสอบ
Code (SQL)
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";
--
-- Database: `test_generic-tests`
--
-- --------------------------------------------------------
--
-- Table structure for table `natural_sort`
--
CREATE TABLE `natural_sort` (
`id` int(11) NOT NULL,
`alphanumeric` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `natural_sort`
--
INSERT INTO `natural_sort` (`id`, `alphanumeric`) VALUES
(1, 'Final fantasy'),
(2, 'Goofy'),
(3, '1A'),
(4, '1a'),
(5, '10b'),
(6, '2d'),
(7, 'กวาง'),
(8, 'กว้าง'),
(9, '12 南新宿');
--
-- Indexes for dumped tables
--
--
-- Indexes for table `natural_sort`
--
ALTER TABLE `natural_sort`
ADD PRIMARY KEY (`id`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `natural_sort`
--
ALTER TABLE `natural_sort`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=10;
COMMIT;
Tag : PHP, MySQL
|
ประวัติการแก้ไข 2021-05-17 10:40:09 2021-05-17 10:44:17
|
 |
 |
 |
 |
Date :
2021-05-17 10:39:15 |
By :
mr.v |
View :
3176 |
Reply :
12 |
|
 |
 |
 |
 |
|
|
|
 |
 |
|
 |
 |
 |
|
|
 |
 |
|
ถ้าเป็นตัวหนังสือไทย ปนตัวเลข
ยังสามารถใช้ ORDER BY ...อะไรไทยๆ จำไม่ได้แล้ว 
|
 |
 |
 |
 |
Date :
2021-05-17 15:35:09 |
By :
พญามัจจุราช |
|
 |
 |
 |
 |
|
|
 |
 |
|
 |
 |
 |
|
|
 |
 |
|

ลองทดสอบกับภาษาไทยแล้ว เหมือนจะไม่ทำงานนะครับ
|
 |
 |
 |
 |
Date :
2021-05-19 05:22:20 |
By :
{Cyberman} |
|
 |
 |
 |
 |
|
|
 |
 |
|
 |
 |
 |
|
|
 |
 |
|

ลองดูกับ phpmyadmin หรือยังครับ?
ถ้าลองแล้วยังไม่ได้ ดูรุ่น phpmyadmin ที่ใหม่ๆหน่อยอย่างของผมใช้ 5.0.4
และถ้าลองแล้วรุ่นใหม่แล้วยังไม่ได้อีก ตรวจรุ่น mariadb, mysql ว่ารุ่นอะไรครับ
ของผม MariaDB 10.4.13-MariaDB
ผมก็ไม่แน่ใจว่ามันต้องการรุ่นอะไรในการรัน เพราะผมไม่ใช่คนเขียนโค้ด ที่มาของโค้ดแปะลิ้งค์อยู่ แต่ถ้าเราสืบจนเจอว่ามันเป็นเพราะอะไรแน่ ก็จะเป็นประโยชน์ต่อคนอื่นที่มาเจอทีหลังแล้วมีปัญหารันไม่ได้ครับผม.
อย่าลืมตรวจ collation ด้วยนะครับ ตารางจะเป็น utf8mb4_unicode_ci
คอลัมน์ alphanumeric ก็จะเป็น utf8mb4_unicode_ci เช่นกัน.
|
ประวัติการแก้ไข 2021-05-19 10:16:41 2021-05-19 10:21:43
 |
 |
 |
 |
Date :
2021-05-19 10:16:06 |
By :
mr.v |
|
 |
 |
 |
 |
|
|
 |
 |
|
 |
 |
 |
|
|
 |
 |
|
ตัวอย่างเพิ่มเติมสำหรับคนที่อาจสงสัยว่าฟังก์ชั่นดังกล่าวมันทำอะไร ผมลองสั่งให้มัน SELECT ออกมาเป็นอีกคอลัมน์หนึ่งเพื่อดูผลของมัน.

Code (SQL)
SELECT `natural_sort`.*, udf_NaturalSortFormat(`alphanumeric`, 10, ".") AS `natsort_alnum` FROM `natural_sort`
ไอเดียนี้อาจนำไปพลิกแพลงใช้ได้กรณีที่ฐานข้อมูลมีข้อมูลเยอะๆ การสั่ง sort order โดยใช้ฟังก์ชั่นแบบนี้อาจทำให้ใช้เวลาและทรัพยากรประมวลผลมาก ก็อาจจะสร้างคอลัมน์อีกอันสำหรับจัดเรียงโดยเฉพาะในฐานข้อมูล เช่นตั้งชื่อว่า natsort_alphanumeric แล้วเวลา update ก็มูลก็ทำการ build ข้อมูลตรงนี้เข้าไปอัพเดททีหนึ่ง เวลา sort order ก็เรียกปกติเลย ORDER BY natsort_alphanumeric ASC ไม่ต้องเรียกใช้ฟังก์ชั่นอีกเพื่อให้มันทำงานไวขึ้นครับ.
|
ประวัติการแก้ไข 2021-05-19 10:29:09
 |
 |
 |
 |
Date :
2021-05-19 10:28:52 |
By :
mr.v |
|
 |
 |
 |
 |
|
|
 |
 |
|
 |
 |
 |
|
|
 |
 |
|
ผมว่ารูปแบบการจัดเรียงที่เป็นแบบเฉพาะ ออกแบบแยก field อักษรกับตัวเลขออกจากกัน น่าจะเร็วที่สุด
ส่วนการจัดเรียง ทั่วไป ผู้ใช้ก็ควรทำความเข้าใจกับ น้ำหนักของอักษร และวิธีการจัดเรียงให้เข้าใจ
เช่นภาษาไทยที่มีลักษณะการใช้ นำหนักของพยัญชนะ เป็นเหลัก ก็ต้องสลับที่อักษรเก็บไว้อีก field
เช่น
ไข่
ขวด
โกง
ตามพจนานุกรมไทยเวลาจัดเรียงจะเป็น
โกง -> กโง
ขวด -> ขวด
ไข่ -> ขไ่
แต่คอมพิวเตอร์ จะเป็น
ขวด
ไข่
โกง
|
ประวัติการแก้ไข 2021-05-19 13:11:53
 |
 |
 |
 |
Date :
2021-05-19 13:11:09 |
By :
Chaidhanan |
|
 |
 |
 |
 |
|
|
 |
 |
|
 |
 |
 |
|
|
 |
 |
|
ตอบความคิดเห็นที่ : 5 เขียนโดย : Chaidhanan เมื่อวันที่ 2021-05-19 13:11:09
รายละเอียดของการตอบ ::
ถ้าจะเรียงตามพจนานุกรมจริงๆ ผมว่าแม้แต่ php ก็ยากครับ ไม่ต้อง mysql หรอก
Code (PHP)
<?php
$words = [];
$words[] = 'ไข่';
$words[] = 'ขวด';
$words[] = 'โกง';
natsort($words);
print_r($words);
Quote:Array
(
[1] => ขวด
[2] => โกง
[0] => ไข่
)
มันจะต้องสร้างฟังก์ชั่นพิเศษขึ้นมาแยกทีละตัวอักษรอีกต่างหาก แยกเป็นตัวอักษรกับสระต่างๆ แค่คิดก็เวียนหัวแล้ว
แต่ถ้ามีเงินอุดหนุนก็น่าค้นคว้า ฮี่ๆ
โดยหลักการจัดเรียงพวกนี้มันจะเรียงทีละลำดับตัวอักษร ก-ฮแล้วจึงตามด้วยสระ ส่วนภาษาอังกฤษก็ A-Z ตามด้วย a-z
ดังนั้นเอาแค่หาวิธีให้มันเรียงได้ธรรมชาติมากที่สุด ไม่ต้องตรงตามหลักด้านอักษรศาสตร์หรือพจนานุกรม เอาแค่นี้ได้ก็หรูแล้วครับ ผมว่านะ.
เพราะโดยธรรมชาติของคอมไม่รู้มันเรียงยังไงของมัน ดูโง่ๆพิกล 10 มาก่อน 2
--- update --------
สำหรับเรื่องการแยกคอลัมน์ตัวเลข ถ้ามันเป็นข้อมูลรูปแบบ abc 123 แบบนี้ก็แยกง่ายอยู่ครับ แต่ถ้าเป็นข้อมูลแบบ abc 123 def 456 แบบนี้จะแยกยากแยกเยอะไปมั้ยครับ?
ตัวอย่าง
Quote:test 1 sub 1
test 10 sub 1
test 2 sub 1
test 1 sub 10
test 1 sub 2
test 2 sub 10
ถ้าแยก field, column มันจะต้องใช้ถึง 4 เลยทีเดียว ไม่ค่อยยืดหยุ่น แต่ถ้า function ที่ผมไปลอกมาข้างบนใช้ได้ผลดี มันจะง่ายและยืดหยุ่นกว่า? คือมันก็สามารถเรียงออกมาได้ถูกต้องเลยครับ
Quote:test 1 sub 1
test 1 sub 2
test 1 sub 10
test 2 sub 1
test 2 sub 10
test 10 sub 1
ส่วนของคุณ phpfastcoding หรือใครที่รันไม่ได้ผล ผมอยากให้ลองปรับให้ mariadb บันทึก log, แจ้ง error โดยดูอ้างอิงการปรับ config จากหน้าเว็บ
https://mariadb.com/kb/en/server-system-variables/
https://mariadb.com/kb/en/error-log/
ตัวอย่างของผม
Code
[mysqld]
datadir=xxx/MariaDB 10.4/data
port=3306
innodb_buffer_pool_size=4089M
collation-server = utf8mb4_unicode_ci
character-set-server=utf8mb4
general_log=0; ใช้เสร็จแล้วปิดไว้ (0) จะหาตรวจ query ค่อยเปิด (1)
general_log_file="xxx/MariaDB 10.4/mysql.log"
log_warnings=1; เดาว่า 1 คือให้มันแจ้งทุกอย่าง
log_error="xxx/MariaDB 10.4/mysql.error.log"
[client]
port=3306
plugin-dir=xxx/MariaDB 10.4/lib/plugin
default-character-set=utf8mb4
จากนั้น restart mysql/mariadb แล้วลองรันผ่านพวก phpmyadmin ดูอีกที. ปกติถ้ามันมีคำสั่งที่ใหม่กว่ารุ่นที่ใช้มันควรจะแจ้ง error
ตัวอย่างผมใช้ mariadb 10.4 แล้วรันคำสั่ง
Code (SQL)
INSERT INTO t2 (id) VALUES (2),(3) RETURNING id,t;
อ้างอิงจาก https://mariadb.com/kb/en/insertreturning/ ซึ่ง INSERT..RETURNING จะเป็นของ 10.5 ดังนั้นมันจึงแจ้ง error
Quote:You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'RETURNING id,t' at line 1
ทีนี้ถ้ามันแจ้ง error แล้วเราจะหาเหตุได้ง่ายขึ้นว่าคำสั่งใกล้ๆมันคืออะไร และต้องใช้รุ่นไหนจึงจะรันได้.
|
ประวัติการแก้ไข 2021-05-19 14:09:41
 |
 |
 |
 |
Date :
2021-05-19 13:44:01 |
By :
mr.v |
|
 |
 |
 |
 |
|
|
 |
 |
|
 |
 |
 |
|
|
 |
 |
|
ขอบคุณสำหรับเทคนิคดีๆครับ
เดี๋ยวผมจะลองรันบนหน้าเว็บ PHP จริงๆดูครับ อาจจะเป็นที่เครื่องมือจัดการฐานข้อมูลก็ได้ครับ
หรืออาจจะเป็นที่ฐานข้อมูลคนละเวอร์ชั่นครับ
|
 |
 |
 |
 |
Date :
2021-05-19 16:01:14 |
By :
{Cyberman} |
|
 |
 |
 |
 |
|
|
 |
 |
|
 |
 |
 |
|
|
 |
 |
|
Guild Line ตัวนี้ดีมากครับ มันสามารถ develop นำไปจัดเรียงข้อมูล ก่อนมาทำ binary search ได้ซึ่งประหยัดเวลามากกว่า 50% สำหรับการค้นหาข้อมูลขนาดใหญ่ ถ้ามีการเรียงข้อมูลอย่างถูกต้องแม่นยำ
Binary Search

|
ประวัติการแก้ไข 2021-05-19 16:43:17
 |
 |
 |
 |
Date :
2021-05-19 16:39:55 |
By :
Genesis™ |
|
 |
 |
 |
 |
|
|
 |
 |
|
 |
 |
 |
|
|
 |
 |
|
ผมทดลองดูกับ MariaDB 10.0.38 แล้วนะครับ ได้ผลตามปกติ

Code (SQL)
SELECT * FROM `natural_sort` ORDER BY udf_NaturalSortFormat(`alphanumeric`, 10, ".") ASC LIMIT 0, 1000
ในภาพใช้ PHPMyAdmin 5.1.0
-- update -------------
MariaDB 10.0.21 ก็ปกติครับ ต่ำกว่านี้หา docker image ไม่ได้แล้วครับ ติดตั้งไม่สำเร็จ exited 139 ไม่มี log เลยจบการทดสอบสำหรับ MariaDB เท่านี้
MySQL 5.5.62 ก็ปกติครับ
|
ประวัติการแก้ไข 2021-05-19 17:16:50 2021-05-19 18:07:42
 |
 |
 |
 |
Date :
2021-05-19 17:03:37 |
By :
mr.v |
|
 |
 |
 |
 |
|
|
 |
 |
|
 |
 |
 |
|
|
 |
 |
|
พอดีไปเจอการเรียงลำดับภาษาไทยแบบคุณ Chaidhanan ว่า
แต่เป็น PHP ล้วนๆ
Code (PHP)
/**
* Sort thai word as UTF-8 without `iconv`.
*
* @link http://web-programming-bookmark.blogspot.com/2013/10/php-sorting-thai-characters.html
*/
function sortthai(&$a){
setlocale(LC_COLLATE, 'th.utf-8');
usort ($a, 'strcoll');
}
/**
* @link http://web-programming-bookmark.blogspot.com/2013/10/php-sorting-thai-characters.html
*/
/*function sortthai(&$a){//สำหรับ utf8
foreach($a as &$i){
$i = iconv('UTF-8','TIS-620',$i);
}
setlocale(LC_COLLATE, 'th');
usort ($a, 'strcoll');
foreach($a as &$i){
$i = iconv('TIS-620','UTF-8',$i);
}
}*/
$words = [
'กรง',
'ไก่',
'โกง',
'โข่ง',
'กำ',
'ขำ',
'ไข่',
'แกะ',
'กวาง',
'คน',
'เข็น',
'ขวด',
];
sortthai($words);
print_r($words);
จะได้
Quote:Array
(
[0] => กรง
[1] => กวาง
[2] => กำ
[3] => แกะ
[4] => โกง
[5] => ไก่
[6] => ขวด
[7] => ขำ
[8] => เข็น
[9] => โข่ง
[10] => ไข่
[11] => คน
)
ที่มา http://web-programming-bookmark.blogspot.com/2013/10/php-sorting-thai-characters.html
|
ประวัติการแก้ไข 2021-05-20 01:24:51
 |
 |
 |
 |
Date :
2021-05-20 01:24:26 |
By :
mr.v |
|
 |
 |
 |
 |
|
|
 |
 |
|
 |
 |
 |
|
|
 |
 |
|
โอ๊ว โวว โหว่ โอ๊ะ โอว 

Code (SQL)
SELECT * FROM `natural_sort` ORDER BY CONVERT(udf_NaturalSortFormat(`alphanumeric`, 10, ".") USING tis620) ASC LIMIT 0, 1000
|
 |
 |
 |
 |
Date :
2021-05-20 01:36:34 |
By :
mr.v |
|
 |
 |
 |
 |
|
|
 |
 |
|
 |
 |
|
|