คำตอบสั้น ๆ: ตรวจสอบและแปลงฐานข้อมูล ตาราง และคอลัมน์ของ MySQL หรือ MariaDB เป็น utf8mb4 เพื่อให้บันทึกภาษาไทยและอีโมจิได้ถูกต้อง พร้อมแก้ปัญหาตัวอักษรเพี้ยน
คู่มือนี้อธิบาย utf8mb4 ทีละขั้นตอน พร้อมคำสั่งที่ใช้ได้จริงและปัญหาที่พบบ่อย
ใน MySQL และ MariaDB ชุดอักขระที่ชื่อ utf8 แบบเดิม (ปัจจุบันเรียกว่า utf8mb3) เก็บอักขระได้สูงสุด 3 ไบต์ต่อตัว ภาษาไทยใช้ 3 ไบต์จึงเก็บได้ แต่อีโมจิและอักขระพิเศษบางกลุ่มใช้ 4 ไบต์ เมื่อผู้ใช้พิมพ์อีโมจิลงในฟอร์ม ข้อมูลจะถูกตัดทิ้งหรือเกิด Error Incorrect string value ส่วนฐานข้อมูลเก่าที่ยังเป็น latin1 ก็มักแสดงภาษาไทยเพี้ยนเมื่อย้ายเซิร์ฟเวอร์
ชุดอักขระที่ควรใช้ในปัจจุบันคือ utf8mb4 ซึ่งเป็น UTF-8 ครบทุกอักขระ บทความนี้อธิบายวิธีตรวจสอบสถานะปัจจุบัน แปลงฐานข้อมูล ตาราง และคอลัมน์เป็น utf8mb4 และตั้งค่าเซิร์ฟเวอร์ให้ฐานข้อมูลใหม่ใช้ utf8mb4 โดยอัตโนมัติ
สิ่งที่ต้องเตรียม
- MySQL 5.7 ขึ้นไป (แนะนำ MySQL 8) หรือ MariaDB 10.3 ขึ้นไป
- ผู้ใช้ฐานข้อมูลที่มีสิทธิ์
ALTERในฐานข้อมูลเป้าหมาย และสิทธิ์ root หรือ sudo หากต้องการแก้ไฟล์ตั้งค่าเซิร์ฟเวอร์ - ไฟล์สำรองฐานข้อมูลล่าสุดที่ทดสอบกู้คืนแล้ว ตาม สำรองและกู้คืนฐานข้อมูล MySQL/MariaDB ด้วย mysqldump
- ช่วงเวลาที่มีผู้ใช้งานน้อย เพราะการแปลงตารางขนาดใหญ่จะสร้างตารางใหม่ทั้งตารางและอาจล็อกการเขียนระหว่างนั้น
หมายเหตุ: การแปลง Character Set เป็นการแก้ไขข้อมูลจริงทุกแถว ถ้าข้อมูลเดิมถูกเก็บผิดรูปแบบ (เช่น ภาษาไทยที่ถูกเก็บในคอลัมน์ latin1) การแปลงแบบปกติจะทำให้ข้อมูลเพี้ยนถาวร ให้สำรองก่อน และทดลองกับสำเนาฐานข้อมูลก่อนทำกับของจริงเสมอ
ขั้นตอนที่ 1: ตรวจสอบ Character Set ปัจจุบัน
เข้า mysql client แล้วดูค่าระดับฐานข้อมูล (แทน shopdb ด้วยชื่อฐานข้อมูลของคุณ)
SELECT SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
FROM information_schema.SCHEMATA
WHERE SCHEMA_NAME = 'shopdb';
ดูระดับตาราง
SELECT TABLE_NAME, ENGINE, ROW_FORMAT, TABLE_COLLATION
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'shopdb' AND TABLE_TYPE = 'BASE TABLE';
และดูคอลัมน์ข้อความที่ยังไม่ใช่ utf8mb4
SELECT TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'shopdb'
AND CHARACTER_SET_NAME IS NOT NULL
AND CHARACTER_SET_NAME <> 'utf8mb4';
ถ้าคำสั่งสุดท้ายไม่คืนแถวใดเลย แปลว่าทุกคอลัมน์เป็น utf8mb4 แล้ว ไม่ต้องแปลงข้อมูล อาจเหลือเพียงการตั้งค่าเซิร์ฟเวอร์และแอปพลิเคชันในขั้นตอนที่ 5-6
ขั้นตอนที่ 2: เลือก Collation
Collation กำหนดวิธีเปรียบเทียบและเรียงลำดับข้อความ ควรเลือกให้เหมือนกันทั้งฐานข้อมูล เพื่อไม่ให้เกิด Error Illegal mix of collations เวลา JOIN ตาราง
| Collation | ใช้ได้กับ | หมายเหตุ |
|---|---|---|
utf8mb4_0900_ai_ci | MySQL 8 เท่านั้น | ค่าเริ่มต้นของ MySQL 8 อิงมาตรฐาน Unicode รุ่นใหม่ แต่ MariaDB ไม่รู้จัก |
utf8mb4_unicode_ci | MySQL และ MariaDB | ตัวเลือกที่ปลอดภัยที่สุดถ้าอาจย้ายข้ามระหว่าง MySQL กับ MariaDB |
utf8mb4_uca1400_ai_ci | MariaDB 10.10 ขึ้นไป | อิงมาตรฐาน Unicode รุ่นใหม่ MariaDB 11.5 ขึ้นไปใช้เป็นค่าเริ่มต้น |
utf8mb4_general_ci | ทั้งสองระบบ | รุ่นเก่า เรียงลำดับไม่แม่นยำเท่า ไม่แนะนำสำหรับงานใหม่ |
ตัวอย่างในบทความนี้ใช้ utf8mb4_unicode_ci ซึ่งใช้ได้ทั้งสองระบบ
ขั้นตอนที่ 3: เปลี่ยนค่าเริ่มต้นของฐานข้อมูล
ALTER DATABASE shopdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
คำสั่งนี้เปลี่ยนเฉพาะค่าเริ่มต้นสำหรับตารางที่จะสร้างใหม่ ตารางเดิมยังไม่เปลี่ยน ต้องทำขั้นตอนถัดไปต่อ
ขั้นตอนที่ 4: แปลงทุกตาราง
คำสั่ง ALTER TABLE ... CONVERT TO CHARACTER SET จะแปลงทั้งค่าเริ่มต้นของตารางและทุกคอลัมน์ข้อความ (CHAR, VARCHAR, TEXT) พร้อมแปลงข้อมูลในคอลัมน์ให้ด้วย ตัวอย่างสำหรับตารางเดียว
ALTER TABLE customers CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ถ้ามีหลายตาราง ให้ใช้ SQL สร้างชุดคำสั่งให้อัตโนมัติ แล้วบันทึกเป็นไฟล์เพื่อตรวจก่อนรัน
mysql -u root -p -N -B -e "SELECT CONCAT('ALTER TABLE \`', TABLE_NAME, '\` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;') FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'shopdb' AND TABLE_TYPE = 'BASE TABLE';" > convert-shopdb.sql
cat convert-shopdb.sql
เมื่อตรวจแล้วว่ารายชื่อตารางถูกต้อง ให้รันไฟล์ที่ได้ (ควรรันใน tmux ถ้าตารางใหญ่)
mysql -u root -p shopdb < convert-shopdb.sql
ถ้าตารางมี Foreign Key เชื่อมกันและเกิด Error เรื่อง Foreign Key ระหว่างแปลง ให้เพิ่มบรรทัด SET foreign_key_checks = 0; ไว้บนสุดของไฟล์ และ SET foreign_key_checks = 1; ไว้ท้ายไฟล์ แล้วรันใหม่