
คุณเคย Insert ข้อมูลภาษาไทยลง MySQL แล้วพอ Query ออกมากลับได้ ??? หรือ àž àž²àžŠàž²àžšàž±àžš ออกมาแทนไหม? ปัญหานี้เกิดจาก Charset ไม่ตรงกัน ระหว่าง Database, Table, PHP Connection และ Client ซึ่งเป็นปัญหาที่พบบ่อยมากในระบบ Hosting ที่ตั้งค่า Default มาจากโรงงาน
บทความนี้อธิบายสาเหตุและวิธีแก้แบบ Step-by-step ตั้งแต่ phpMyAdmin ไปจนถึง PHP Connection โดยไม่ต้องแตะ MySQL Config Server ซึ่งโดยปกติผู้ใช้ Shared Hosting ไม่มีสิทธิ์แก้ไข
สรุปสั้น: ปัญหาภาษาไทยเป็น ? แก้ได้ด้วยการตั้ง Charset เป็น utf8mb4 และ Collation เป็น utf8mb4_unicode_ci ทั้งที่ Database, Table, Column และ PHP Connection
1. ปัญหา ? ใน MySQL เกิดจากอะไร?
MySQL จัดเก็บข้อมูลในรูปแบบ Binary โดยใช้ Charset (ชุดตัวอักษร) และ Collation (กฎการเปรียบเทียบ/เรียงลำดับ) กำหนดว่า "ตัวอักษรนี้คือ byte อะไร" ปัญหาเกิดขึ้นเมื่อ:
- PHP ส่งข้อมูลไปใน UTF-8 แต่ Database รับเป็น Latin1
- Table ถูกสร้างด้วย Charset ผิด (เช่น
latin1) - PHP Connection ไม่ได้ตั้ง
SET NAMES utf8mb4 - Column บางอันมี Charset ต่างจาก Table
เมื่อ MySQL ไม่สามารถแปลง byte ที่รับมาเป็นตัวอักษรที่ Charset ของ Table รองรับได้ มันจะแทนที่ด้วย ? หรือแสดงเป็นข้อมูลเสียหาย
2. Charset คืออะไร — Latin1 vs UTF-8 vs utf8mb4
MySQL มี Charset ให้เลือกหลายแบบ แต่ที่ต้องรู้จักมีดังนี้:
| Charset | รองรับภาษาไทย | Emoji | แนะนำ |
|---|---|---|---|
latin1 | ไม่รองรับ | ไม่รองรับ | ห้ามใช้ |
utf8 (MySQL) | รองรับ | ไม่รองรับ (max 3 bytes) | ใช้ได้แต่ไม่สมบูรณ์ |
utf8mb4 | รองรับ | รองรับ (4 bytes) | แนะนำ |
ข้อสำคัญ: utf8 ใน MySQL ไม่ใช่ UTF-8 มาตรฐาน! มันรองรับแค่ 3 bytes ต่อตัวอักษร ทำให้ Emoji หรืออักษรบางภาษาเสียหาย ให้ใช้ utf8mb4 เสมอ
3. วิธีแก้ด้วย phpMyAdmin — ALTER TABLE
เข้า phpMyAdmin ผ่าน DirectAdmin แล้วทำตามขั้นตอน:
ขั้นที่ 1: แก้ไข Database Charset
คลิก Database ที่ต้องการ แล้วไปที่แท็บ Operations เลื่อนลงหา "Collation" แล้วเลือก utf8mb4_unicode_ci กด Go
หรือใช้ SQL ใน Query tab:
ALTER DATABASE `ชื่อ_database` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ขั้นที่ 2: แก้ไข Table ทุกตาราง
ถ้ามีหลาย Table ให้รันคำสั่งนี้สำหรับแต่ละ Table (แทนชื่อ table ให้ถูกต้อง):
ALTER TABLE `ชื่อ_table` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
คำสั่ง CONVERT TO จะแปลง Charset ของทุก Column ใน Table พร้อมกัน ต่างจาก DEFAULT CHARACTER SET ที่แก้แค่ Default ของ Table ใหม่
⚠️ ควร Backup ก่อนเสมอ: ก่อนรัน ALTER TABLE ให้ Export Database ออกเป็น .sql ไว้ก่อน กรณีข้อมูลเสียหายระหว่างการแปลง Charset
4. ตั้งค่าใน PHP Connection
แม้ Database จะใช้ utf8mb4 แล้ว แต่ถ้า PHP Connection ไม่บอก MySQL ว่าจะส่งข้อมูลใน Charset อะไร ก็ยังมีปัญหาได้ ตั้งค่าดังนี้:
วิธีที่ 1: PDO (แนะนำ)
// PDO connection พร้อม charset
$dsn = "mysql:host=localhost;dbname=mydb;charset=utf8mb4";
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"
]);วิธีที่ 2: mysqli
// mysqli connection $conn = new mysqli($host, $user, $pass, $dbname); $conn->set_charset("utf8mb4"); // หรือใช้ query $conn->query("SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'")
5. ตั้งค่าถาวรในไฟล์ PHP (Hosting)
บน Shared Hosting ที่ไม่มีสิทธิ์แก้ MySQL config สามารถตั้ง Default Charset ผ่าน PHP ได้ โดยเพิ่มบรรทัดนี้ต้นไฟล์ที่ Connect Database:
<?php // ตั้ง Timezone ตามกฎ AsiaGB date_default_timezone_set('Asia/Bangkok'); // ตั้ง Charset สำหรับ MySQL $dsn = "mysql:host=localhost;dbname=mydb;charset=utf8mb4"; $pdo = new PDO($dsn, $user, $pass); $pdo->exec("SET NAMES utf8mb4"); $pdo->exec("SET CHARACTER SET utf8mb4"); $pdo->exec("SET SESSION collation_connection = 'utf8mb4_unicode_ci'");
WordPress: ถ้าใช้ WordPress ให้แก้ wp-config.php บรรทัด define('DB_CHARSET', 'utf8mb4'); และ define('DB_COLLATE', 'utf8mb4_unicode_ci'); — ปกติ WordPress ตั้งค่านี้มาให้แล้วตั้งแต่ version 4.2
6. ทำไมภาษาไทยกลายเป็น ??? — charset/collation mismatch ทุกชั้น (DB, table, connection, HTML)
ความเข้าใจสำคัญที่สุดของปัญหานี้คือ "ภาษาไทยกลายเป็น ???" ไม่ได้เกิดจากจุดเดียว แต่เกิดจาก ความไม่ตรงกันของ charset ในหลายชั้น ที่ข้อมูลเดินทางผ่าน ตั้งแต่ฟอร์ม HTML ที่ผู้ใช้พิมพ์ ไปจนถึง byte ที่เก็บลงดิสก์จริง ถ้าชั้นใดชั้นหนึ่งตีความ byte ผิด ตัวอักษรไทยก็จะเสียหายทันที โดยอาการจะต่างกันตามว่าผิดที่ชั้นไหน
ลองนึกภาพข้อมูลภาษาไทยเดินทางเป็น 4 ชั้นตามลำดับนี้ แต่ละชั้นต้องตกลงกันว่าจะใช้ utf8mb4 เหมือนกัน:
| ชั้น | ตั้งค่าที่ไหน | ถ้าตั้งผิดจะเกิดอะไร |
|---|---|---|
| 1. HTML / ฟอร์ม | <meta charset="UTF-8"> และ Content-Type header | เบราว์เซอร์ส่ง byte ผิด encoding มาตั้งแต่ต้น |
| 2. PHP Connection | SET NAMES utf8mb4 / charset=utf8mb4 ใน DSN | MySQL เข้าใจว่า client พูดภาษา latin1 → แปลง byte ผิด |
| 3. Table / Column | CHARACTER SET utf8mb4 ตอน CREATE/ALTER | ช่องเก็บข้อมูลรับได้แค่ latin1 → ตัวที่เกิน byte กลายเป็น ? |
| 4. Database | ALTER DATABASE ... CHARACTER SET utf8mb4 | table ใหม่ที่สร้างต่อจากนี้ inherit charset ผิดต่อไปเรื่อยๆ |
อาการที่พบบ่อยและวิธีอ่านว่าผิดชั้นไหน:
- ขึ้น
???(เครื่องหมายคำถามล้วน): column หรือ table เป็นlatin1— MySQL รับ byte ของ utf8mb4 มาแล้วเก็บไม่ได้ จึงทิ้งเป็น?ข้อมูลเสียหายถาวร กู้คืนไม่ได้นอกจากมี backup - ขึ้นตัวอักษรประหลาด เช่น
ภาษา(mojibake): ข้อมูล byte ยังครบดี แต่ connection ตีความ encoding ผิด — แก้ได้ด้วยการตั้งSET NAMESให้ถูก หรือแปลงข้อมูล (ดูหัวข้อ double-encoding ด้านล่าง) - ภาษาไทยปกติแต่ emoji เป็น
?: ใช้utf8(3 ไบต์) แทนutf8mb4(4 ไบต์) — emoji และอักษรบางตัวต้องการ 4 ไบต์
กฎทอง: ตรวจให้ครบทั้ง 4 ชั้นเป็น utf8mb4 เหมือนกันหมด — ถ้าแก้แค่ table แต่ลืม connection (หรือกลับกัน) ปัญหาจะยังอยู่ เพราะข้อมูลต้องผ่านทุกชั้นโดยไม่ตกหล่น
7. ตั้ง charset ให้ถูก utf8mb4 ทุกชั้น (DB · table · connection · HTML)
หัวข้อนี้รวมคำสั่งทั้งหมดที่ต้องรันให้ครบทุกชั้น เรียงตามลำดับการตรวจสอบจริง เริ่มจากตรวจสถานะปัจจุบันก่อนว่าชั้นไหนเป็น latin1 อยู่:
ขั้นที่ 1: ตรวจ charset ปัจจุบันของทุกชั้น
เปิด phpMyAdmin ใน DirectAdmin ไปที่แท็บ SQL แล้วรันคำสั่งตรวจสอบ:
-- ดู charset/collation ของ Database SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = 'ชื่อ_database'; -- ดู charset ของทุก table ใน database SELECT TABLE_NAME, TABLE_COLLATION FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'ชื่อ_database'; -- ดู charset ของแต่ละ column ใน table SHOW FULL COLUMNS FROM `ชื่อ_table`; -- ดูค่า charset ที่ connection กำลังใช้อยู่ SHOW VARIABLES LIKE 'character_set%'; SHOW VARIABLES LIKE 'collation%';
ถ้าผลลัพธ์ของ character_set_client, character_set_connection หรือ character_set_results ขึ้นเป็น latin1 แสดงว่าชั้น connection ยังไม่ถูก ต้องแก้ที่โค้ด PHP
ขั้นที่ 2: ตั้ง charset ให้ครบทุกชั้นด้วย SQL
-- ชั้น Database ALTER DATABASE `ชื่อ_database` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- ชั้น Table (CONVERT TO แปลงทุก column พร้อมกัน) ALTER TABLE `ชื่อ_table` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- ชั้น Connection (รันทุกครั้งหลัง connect) SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;
ส่วนชั้น HTML ให้แน่ใจว่าหน้าเว็บที่แสดงผลและฟอร์มรับข้อมูลมี meta charset ถูกต้องในส่วน <head>:
<meta charset="UTF-8">
// และฝั่ง PHP ส่ง header ให้ตรงกัน
header('Content-Type: text/html; charset=utf-8');⚠️ ลำดับสำคัญ: ตั้ง connection ให้เป็น utf8mb4 ก่อน แล้วค่อย Insert ข้อมูลใหม่ ถ้า Insert ตอน connection ยังเป็น latin1 ข้อมูลที่เก็บลงไปจะเพี้ยนตั้งแต่ต้น แม้ table จะเป็น utf8mb4 แล้วก็ตาม
8. แปลง table/column ที่เป็น latin1 มาเป็น utf8mb4 (ALTER, CONVERT)
เมื่อมี table เก่าที่ถูกสร้างด้วย latin1 การแปลงต้องระวังเป็นพิเศษ เพราะมี 2 กรณีที่ต้องจัดการต่างกัน: (1) data ใน column เป็น ภาษาไทยที่ถูกต้องอยู่แล้ว แค่ป้าย charset ผิด หรือ (2) data เพี้ยนไปแล้ว จากการเก็บผิด encoding
กรณีปกติ: แปลงทั้ง table ด้วย CONVERT TO
ถ้าข้อมูลเดิมแสดงผลถูกต้อง (ผ่าน connection ที่ตั้ง charset ตรงกับตอน insert) ให้ใช้ CONVERT TO ซึ่ง MySQL จะแปลง byte ของทุก column ให้เป็น utf8mb4 อย่างถูกต้อง:
ALTER TABLE `posts` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
แปลงทุก table ในครั้งเดียว (สร้างคำสั่งอัตโนมัติ)
ถ้ามีหลายสิบ table ให้รัน query นี้เพื่อ generate คำสั่ง ALTER ของทุก table แล้ว copy ผลลัพธ์ไปรันอีกที:
SELECT CONCAT( 'ALTER TABLE `', TABLE_NAME, '` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;' ) AS sql_command FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'ชื่อ_database' AND TABLE_TYPE = 'BASE TABLE';
กรณีพิเศษ: แปลงทีละ column ผ่าน BLOB (กัน data หาย)
ถ้า CONVERT TO ทำให้ข้อมูลเสียหาย (เพราะข้อมูลเดิมเป็น utf8mb4 byte แต่ป้ายว่า latin1 อยู่แล้ว) ให้ใช้เทคนิคแปลงผ่าน BLOB ซึ่งบอก MySQL ว่า "อย่าแตะ byte แค่เปลี่ยนป้าย charset":
// ขั้นที่ 1: เปลี่ยน column เป็น BLOB (เก็บ byte ดิบไว้) ALTER TABLE `posts` MODIFY `content` BLOB; // ขั้นที่ 2: เปลี่ยนกลับเป็น TEXT พร้อมป้าย utf8mb4 ALTER TABLE `posts` MODIFY `content` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
เลือกวิธีไหน: ถ้าข้อมูลเดิม แสดงผลถูก ใน phpMyAdmin (ที่ default เป็น utf8mb4) ให้ใช้ CONVERT TO ปกติ — แต่ถ้าข้อมูลเดิม แสดงผลถูกเฉพาะตอนตั้ง connection เป็น latin1 ให้ใช้เทคนิค BLOB เพื่อรักษา byte เดิม
9. แก้ข้อมูลที่เพี้ยนไปแล้ว (double-encoding) + ตั้งใน WordPress wp-config
Double-encoding คืออาการที่ข้อมูล utf8mb4 ถูกตีความเป็น latin1 แล้วถูก encode เป็น utf8 อีกรอบ ทำให้ตัวอักษรไทย 1 ตัวกลายเป็นชุดตัวอักษรประหลาดยาวๆ เช่น ภาษาไทย แทนคำว่า "ภาษาไทย" — กรณีนี้ data ยังไม่หาย สามารถกู้คืนได้
วิธีกู้ข้อมูล double-encoded
เทคนิคคือบอก MySQL ให้ตีความ byte ปัจจุบันเป็น latin1 (เพื่อถอด encode ชั้นนอกออก) แล้วอ่านกลับเป็น utf8mb4:
// ทดสอบดูผลก่อน (ยังไม่แก้ข้อมูลจริง) SELECT CONVERT(CAST(CONVERT(`content` USING latin1) AS BINARY) USING utf8mb4) FROM `posts` LIMIT 5; // ถ้าผลถูกต้องแล้ว ค่อย UPDATE จริง (Backup ก่อนเสมอ!) UPDATE `posts` SET `content` = CONVERT(CAST(CONVERT(`content` USING latin1) AS BINARY) USING utf8mb4);
⚠️ ห้ามรัน UPDATE ซ้ำ: คำสั่งกู้ double-encoding ต้องรัน ครั้งเดียว เท่านั้น ถ้ารันซ้ำข้อมูลจะเพี้ยนอีกชั้น ให้ Export backup ก่อน แล้วทดสอบด้วย SELECT จนแน่ใจก่อน UPDATE
ตั้งค่าใน WordPress (wp-config.php)
WordPress ตั้งค่า charset มาให้แล้วตั้งแต่ version 4.2 แต่ถ้าย้ายเว็บเก่ามา ให้ตรวจ wp-config.php ให้มีบรรทัดนี้:
<?php
// charset ของ WordPress database
define('DB_CHARSET', 'utf8mb4');
define('DB_COLLATE', 'utf8mb4_unicode_ci');ถ้า table ของ WordPress เก่ายังเป็น utf8 หรือ latin1 ให้รัน ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4 กับทุก table ของ WordPress (เช่น wp_posts, wp_postmeta, wp_comments) หรือใช้คำสั่ง WP-CLI ถ้า hosting รองรับ:
# แปลง charset ทุก table ผ่าน WP-CLI
wp db query "ALTER DATABASE \`$(wp config get DB_NAME)\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"WordPress บน DirectAdmin: ถ้าไม่มี SSH/WP-CLI สามารถแปลงทุก table ผ่าน phpMyAdmin ได้โดยใช้ query generate คำสั่งในหัวข้อที่ 8 แล้ว copy ไปรัน — ได้ผลเหมือนกัน
10. Checklist แก้ภาษาไทยใน MySQL
- Backup Database ก่อนทุกครั้ง
- แก้ Database Charset เป็น
utf8mb4ใน phpMyAdmin - รัน
ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4ทุก Table - เพิ่ม
charset=utf8mb4ใน DSN ของ PDO connection - เรียก
SET NAMES utf8mb4หลัง Connect ทุกครั้ง - ทดสอบ Insert ภาษาไทย แล้ว SELECT ออกมาดูว่าถูกต้อง
- ตรวจ Column ที่มีปัญหาด้วย
SHOW FULL COLUMNS FROM table_name
คำถามที่พบบ่อย (FAQ)
ทำไมข้อมูลภาษาไทยใน phpMyAdmin ดูปกติ แต่หน้าเว็บแสดงเป็น ???
เพราะ phpMyAdmin ตั้ง connection เป็น utf8mb4 มาให้ตั้งแต่แรก จึงอ่านข้อมูลออกถูก แต่โค้ด PHP ของเว็บคุณอาจไม่ได้ตั้ง SET NAMES utf8mb4 หลัง connect ทำให้ MySQL ส่งข้อมูลกลับมาในภาษา latin1 และเพี้ยน วิธีแก้คือเพิ่ม charset=utf8mb4 ใน DSN ของ PDO หรือเรียก $conn->set_charset("utf8mb4") ของ mysqli ทุกครั้ง
แปลงเป็น utf8mb4 แล้วข้อมูลเดิมที่เป็น ??? จะกลับมาไหม?
ไม่กลับมา ถ้าข้อมูลเก็บเป็น ? ไปแล้ว แปลว่า byte ต้นฉบับถูกทิ้งตั้งแต่ตอน Insert (column เป็น latin1 รับ byte ของ utf8 ไม่ได้) ข้อมูลนั้นเสียหายถาวร ต้องกู้จาก backup เท่านั้น — แต่ถ้าเป็นอาการ mojibake (ภาษา) byte ยังครบ กู้คืนได้ด้วยเทคนิคในหัวข้อ double-encoding
ควรใช้ utf8mb4_unicode_ci หรือ utf8mb4_general_ci?
แนะนำ utf8mb4_unicode_ci เพราะเรียงลำดับและเปรียบเทียบตัวอักษรหลายภาษาได้แม่นยำกว่าตามมาตรฐาน Unicode ส่วน utf8mb4_general_ci เร็วกว่าเล็กน้อยแต่ความถูกต้องด้อยกว่า สำหรับเว็บที่มีภาษาไทยปนภาษาอื่น unicode_ci เหมาะกว่า (MySQL 8 มี utf8mb4_0900_ai_ci ที่ใหม่กว่าด้วย)
ต้อง Backup ก่อนแปลง charset จริงไหม?
จำเป็นมาก โดยเฉพาะคำสั่ง CONVERT TO และ UPDATE กู้ double-encoding ที่แก้ข้อมูลจริง ให้ Export database เป็นไฟล์ .sql ผ่าน phpMyAdmin ใน DirectAdmin ก่อนทุกครั้ง ถ้าผลไม่ตรงตามคาดจะได้ Restore กลับได้
Hosting รองรับ MySQL พร้อม phpMyAdmin ครบครัน
AsiaGB Hosting เริ่มต้น 500 บาท/ปี พร้อม DirectAdmin, phpMyAdmin, PHP 7.4/8.2/8.3 และ SSD Storage รองรับการพัฒนาเว็บทุกรูปแบบ
ดูแพ็กเกจ Hosting