Data Types ใน MySQL — เลือกใช้ให้ถูกต้อง

การเลือก Data Type ที่เหมาะสมเป็นหนึ่งในการตัดสินใจสำคัญที่สุดเมื่อออกแบบฐานข้อมูล เพราะส่งผลโดยตรงต่อพื้นที่จัดเก็บ ประสิทธิภาพในการ Query และความถูกต้องของข้อมูล การเลือกผิดอาจทำให้สิ้นเปลืองเนื้อที่ ประมวลผลช้า หรือเกิดข้อมูลผิดพลาดที่แก้ไขได้ยากในภายหลัง

บทความนี้จะอธิบาย Data Type ทุกกลุ่มที่มีให้เลือกใช้อย่างละเอียด ทั้งตัวเลข ข้อความ วันเวลา และชนิดพิเศษ พร้อมแนวทางเลือกใช้ให้เหมาะกับแต่ละสถานการณ์

ทำไมการเลือก Data Type ถึงสำคัญ

ชนิดข้อมูลที่เลือกกำหนดสามสิ่งหลัก — พื้นที่จัดเก็บต่อแถว ความสามารถในการสร้าง Index อย่างมีประสิทธิภาพ และกฎ Validation ที่ระบบบังคับใช้อัตโนมัติ ตัวอย่างเช่น การใช้ BIGINT สำหรับคอลัมน์ที่เก็บค่าไม่เกิน 255 จะสิ้นเปลืองพื้นที่ถึง 7 เท่าเมื่อเทียบกับ TINYINT และเมื่อตารางมีหลายล้านแถว ความแตกต่างนี้ส่งผลให้ Index มีขนาดใหญ่ขึ้น Cache ใน Memory ได้น้อยลง และ Query ช้าลงอย่างเห็นได้ชัด

กลุ่มตัวเลขจำนวนเต็ม (Integer Types)

กลุ่มนี้ใช้เก็บตัวเลขที่ไม่มีจุดทศนิยม แต่ละชนิดมีขนาดจัดเก็บและช่วงค่าต่างกัน

TINYINT — ใช้พื้นที่ 1 Byte รองรับค่า -128 ถึง 127 (หรือ 0 ถึง 255 แบบ UNSIGNED) เหมาะกับข้อมูลที่มีค่าน้อย เช่น สถานะ (status), อายุ, หรือ Boolean (0/1)

SMALLINT — ใช้พื้นที่ 2 Bytes รองรับค่า -32,768 ถึง 32,767 เหมาะกับรหัสที่มีจำนวนจำกัด เช่น รหัสจังหวัด หรือ Year ที่ไม่ต้องการความแม่นยำระดับวันเวลา

MEDIUMINT — ใช้พื้นที่ 3 Bytes รองรับค่าถึงประมาณ 16 ล้าน เหมาะกับ Primary Key ของตารางขนาดกลาง

INT — ใช้พื้นที่ 4 Bytes รองรับค่าถึงประมาณ 2.1 พันล้าน เป็นตัวเลือกมาตรฐานสำหรับ Primary Key และตัวเลขทั่วไป ใช้บ่อยที่สุดในทุกระบบ

BIGINT — ใช้พื้นที่ 8 Bytes รองรับค่ามหาศาลถึง 9.2 x 10^18 จำเป็นเฉพาะตารางที่มีแถวเกิน 2 พันล้าน หรือเก็บค่าขนาดใหญ่เช่น Unix Timestamp ในหน่วย Microsecond

ทุกชนิดรองรับ UNSIGNED ที่เพิ่มช่วงค่าบวกเป็นสองเท่า แต่ตัด Negative ออก เช่น INT UNSIGNED รองรับ 0 ถึง 4.29 พันล้าน เหมาะกับ Auto Increment ID ที่ไม่มีวันติดลบ

กลุ่มตัวเลขทศนิยม (Decimal Types)

DECIMAL(M,D) — เก็บตัวเลขทศนิยมแบบ Exact (Fixed-point) ไม่มีปัญหา Rounding Error เหมาะกับข้อมูลทางการเงินที่ต้องการความแม่นยำสูงสุด M คือจำนวนหลักทั้งหมด D คือจำนวนหลักหลังจุดทศนิยม ตัวอย่างเช่น DECIMAL(10,2) เก็บค่าได้ถึง 99,999,999.99 ใช้พื้นที่ประมาณ 5 Bytes

FLOAT — เก็บตัวเลขทศนิยมแบบ Approximate (Floating-point) ขนาด 4 Bytes มีความแม่นยำประมาณ 7 หลัก เหมาะกับข้อมูลทางวิทยาศาสตร์หรือค่าที่ยอมรับ Rounding Error เล็กน้อยได้ เช่น คะแนน rating หรือค่า sensor

DOUBLE — เหมือน FLOAT แต่ขนาด 8 Bytes มีความแม่นยำประมาณ 15 หลัก ใช้เมื่อต้องการความแม่นยำมากกว่า FLOAT แต่ไม่ถึงระดับ DECIMAL

กฎง่าย ๆ คือ ถ้าเกี่ยวกับเงินหรือตัวเลขที่ต้องคำนวณแล้วตรงเป๊ะ ให้ใช้ DECIMAL เสมอ ถ้าเป็นค่าทางวิทยาศาสตร์หรือ Approximate ได้ ใช้ FLOAT หรือ DOUBLE

กลุ่มข้อความ (String Types)

CHAR(N) — ข้อความความยาวคงที่ N ตัวอักษร (สูงสุด 255) ถ้าข้อมูลสั้นกว่า N จะเติม Space ให้เต็ม ข้อดีคือ Index ได้เร็วเพราะขนาดคงที่ เหมาะกับข้อมูลที่มีความยาวตายตัว เช่น รหัสประเทศ (CHAR(2)), รหัส ISO Currency (CHAR(3)), หรือ MD5 Hash (CHAR(32))

VARCHAR(N) — ข้อความความยาวไม่คงที่ สูงสุด N ตัวอักษร (N สูงสุด 65,535 แต่ถูกจำกัดด้วย Row Size) ใช้พื้นที่เท่าที่จำเป็นจริง บวก 1-2 Bytes สำหรับเก็บความยาว เป็นตัวเลือกมาตรฐานสำหรับข้อความทั่วไป เช่น ชื่อ อีเมล URL

TEXT — ข้อความยาวสูงสุด 65,535 ตัวอักษร ไม่สามารถตั้ง Default Value ได้ และการใช้ใน Index ต้องกำหนด Prefix Length เสมอ เหมาะกับเนื้อหาที่ยาวไม่แน่นอน เช่น คำอธิบายสินค้า หรือ Comment

MEDIUMTEXT — รองรับข้อความยาวถึง 16 MB เหมาะกับเนื้อหาบทความหรือ Blog Post

LONGTEXT — รองรับข้อความยาวถึง 4 GB ใช้เฉพาะกรณีพิเศษ เช่น เก็บ JSON ขนาดใหญ่หรือ Log Files

สิ่งสำคัญที่ต้องระวังคือ VARCHAR เก็บข้อมูลใน Row โดยตรง จึง Query ได้เร็ว ส่วน TEXT ถูกเก็บแยก (Off-page Storage) ทำให้เข้าถึงช้ากว่า ดังนั้นถ้าข้อมูลยาวไม่เกิน 1,000-2,000 ตัวอักษร ควรใช้ VARCHAR มากกว่า TEXT

กลุ่มวันที่และเวลา (Date/Time Types)

DATE — เก็บเฉพาะวันที่ในรูปแบบ YYYY-MM-DD ใช้พื้นที่ 3 Bytes รองรับ 1000-01-01 ถึง 9999-12-31 เหมาะกับวันเกิด วันหมดอายุ หรือวันที่ไม่ต้องการเวลา

TIME — เก็บเฉพาะเวลาในรูปแบบ HH:MM:SS ใช้พื้นที่ 3 Bytes รองรับช่วง -838:59:59 ถึง 838:59:59 ใช้ได้ทั้งเวลาปกติและระยะเวลา (Duration)

DATETIME — เก็บทั้งวันที่และเวลา ใช้พื้นที่ 5 Bytes (หรือ 8 Bytes ถ้ามี Fractional Seconds) ค่าที่เก็บไม่ขึ้นกับ Timezone เหมาะกับเวลาที่ต้องคงค่าเดิมเสมอ เช่น วันนัดหมาย หรือวันที่ในเอกสาร

TIMESTAMP — เก็บทั้งวันที่และเวลาเช่นกัน แต่ใช้พื้นที่เพียง 4 Bytes โดยแปลงเป็น UTC เมื่อเก็บ และแปลงกลับตาม Timezone ของ Session เมื่อดึงข้อมูล รองรับค่าถึง 2038-01-19 เท่านั้น เหมาะกับ created_at และ updated_at ที่ต้องการให้แสดงเวลาตาม Timezone ของผู้ใช้

YEAR — เก็บเฉพาะปี ใช้พื้นที่ 1 Byte รองรับ 1901 ถึง 2155 เหมาะกับข้อมูลที่ต้องการเฉพาะปี เช่น ปีที่ผลิตหรือปีที่จบการศึกษา

DATETIME กับ TIMESTAMP เลือกอะไรดี

คำถามนี้เป็นที่ถกเถียงกันบ่อย กฎง่าย ๆ คือ — ถ้าเป็นเวลาที่ระบบบันทึกอัตโนมัติ (เช่น เวลาสร้างหรืออัพเดตแถว) ให้ใช้ TIMESTAMP เพราะรองรับ Timezone Conversion และตั้ง DEFAULT CURRENT_TIMESTAMP ได้สะดวก แต่ถ้าเป็นเวลาที่ผู้ใช้กำหนดเอง (เช่น วันนัดหมาย วันจอง) ที่ต้องคงค่าเดิมไม่ว่า Timezone จะเปลี่ยน ให้ใช้ DATETIME

กลุ่ม Binary (Binary Types)

BINARY(N) และ VARBINARY(N) — เหมือน CHAR/VARCHAR แต่เก็บข้อมูลเป็น Byte String ไม่มี Character Set หรือ Collation เหมาะกับ Hash, UUID ในรูปแบบ Binary (16 Bytes แทน 36 ตัวอักษร), หรือ Encrypted Data

BLOB (Binary Large Object) — มี 4 ขนาดเหมือน TEXT คือ TINYBLOB, BLOB, MEDIUMBLOB และ LONGBLOB เหมาะกับการเก็บไฟล์ Binary เช่น รูปภาพหรือเอกสาร แม้ว่าในทางปฏิบัติมักแนะนำให้เก็บไฟล์ใน Object Storage แล้วเก็บเฉพาะ Path ไว้ในตารางแทน

ชนิดพิเศษ (Special Types)

ENUM — กำหนดรายการค่าที่เป็นไปได้ไว้ล่วงหน้า เช่น ENUM(‘active’,’inactive’,’banned’) ใช้พื้นที่เพียง 1-2 Bytes เท่านั้นไม่ว่าข้อความจะยาวเท่าไร เพราะเก็บเป็น Index Number ภายใน ข้อจำกัดคือการเพิ่มค่าใหม่ต้อง ALTER TABLE

SET — คล้าย ENUM แต่เก็บได้หลายค่าพร้อมกัน เช่น SET(‘read’,’write’,’execute’) ค่าหนึ่งแถวอาจเป็น ‘read,write’ ใช้ Bitmask ในการเก็บ จึงรองรับสูงสุด 64 ค่า เหมาะกับ Permission Flags แต่ไม่ค่อยนิยมใช้เพราะ Query ซับซ้อน

JSON — เก็บข้อมูล JSON โดย Validate รูปแบบให้อัตโนมัติ รองรับ Function เช่น JSON_EXTRACT(), JSON_SET(), JSON_CONTAINS() และสร้าง Generated Column จาก JSON Path เพื่อทำ Index ได้ เหมาะกับข้อมูลที่มีโครงสร้างยืดหยุ่น เช่น Product Attributes ที่แต่ละสินค้ามีฟิลด์ต่างกัน หรือ Configuration Settings

BOOLEAN — ใน MySQL นั้น BOOLEAN เป็นแค่ Alias ของ TINYINT(1) โดย TRUE = 1 และ FALSE = 0 เข้าใจไว้ว่าจริง ๆ แล้วเก็บเป็นตัวเลข ไม่ใช่ค่า True/False แบบภาษาโปรแกรม

Spatial Types สำหรับข้อมูลภูมิศาสตร์

สำหรับแอปพลิเคชันที่ต้องจัดการข้อมูลตำแหน่ง มี Spatial Data Types ให้เลือก เช่น POINT สำหรับพิกัดจุดเดียว, LINESTRING สำหรับเส้นทาง, POLYGON สำหรับพื้นที่ปิด และ GEOMETRY สำหรับรูปทรงเรขาคณิตทั่วไป รองรับ Spatial Index ด้วย R-Tree ที่ค้นหาตำแหน่งใกล้เคียงได้อย่างรวดเร็ว

Character Set กับ Collation

Character Set กำหนดว่าข้อความเก็บเป็น Encoding ใด ส่วน Collation กำหนดกฎการเรียงลำดับและเปรียบเทียบ แนะนำให้ใช้ utf8mb4 เป็น Character Set มาตรฐาน เพราะรองรับทุกภาษารวมถึง Emoji (utf8 ปกติรองรับเพียง 3 Bytes ต่อตัวอักษร ไม่ครอบคลุม Emoji)

สำหรับ Collation แนะนำ utf8mb4_unicode_ci สำหรับการเรียงลำดับที่ถูกต้องตาม Unicode Standard หรือ utf8mb4_0900_ai_ci (Default ใน 8.0) ที่เร็วกว่า ส่วน utf8mb4_bin ใช้เมื่อต้องการเปรียบเทียบแบบ Case-sensitive ทุกกรณี

แนวทางเลือก Data Type ให้เหมาะสม

เลือกขนาดเล็กที่สุดที่เพียงพอ — ถ้าค่าไม่เกิน 255 ใช้ TINYINT แทน INT ถ้ารหัสมีแค่ 2 ตัวอักษร ใช้ CHAR(2) แทน VARCHAR(255) ยิ่งขนาดเล็ก Index ยิ่งกระทัดรัด Query ยิ่งเร็ว

เงินต้องใช้ DECIMAL เท่านั้น — ห้ามใช้ FLOAT หรือ DOUBLE กับข้อมูลการเงิน เพราะ Rounding Error แม้เล็กน้อยจะสะสมจนผิดพลาดได้ ตัวอย่างเช่น DECIMAL(12,2) สำหรับสกุลเงินทั่วไป หรือ DECIMAL(18,8) สำหรับ Cryptocurrency

VARCHAR ดีกว่า TEXT ในกรณีส่วนใหญ่ — ใช้ TEXT เฉพาะเมื่อข้อมูลยาวเกิน 65,535 Bytes จริง ๆ หรือเมื่อไม่สามารถประมาณความยาวสูงสุดได้เลย

UNSIGNED สำหรับค่าที่ไม่ติดลบ — Auto Increment ID, จำนวนสินค้า, อายุ ค่าเหล่านี้ไม่มีวันติดลบ การใช้ UNSIGNED ช่วยขยายช่วงค่าบวกเป็นสองเท่าโดยไม่เพิ่มพื้นที่

UUID เก็บเป็น BINARY(16) — แทนที่จะเก็บ UUID เป็น CHAR(36) ซึ่งใช้พื้นที่ 36 Bytes ให้ใช้ UUID_TO_BIN() แปลงเป็น BINARY(16) ซึ่งเล็กกว่าครึ่ง Index เร็วกว่า และเปรียบเทียบเร็วกว่ามาก

ENUM ใช้เมื่อค่าคงที่จริง ๆ — ถ้ารายการค่าอาจเปลี่ยนแปลงบ่อย ให้สร้างตารางแยก (Lookup Table) แทน เพราะการ ALTER TABLE เพื่อเพิ่มค่า ENUM ในตารางขนาดใหญ่อาจใช้เวลานาน

ข้อผิดพลาดที่พบบ่อย

ใช้ VARCHAR(255) ทุกคอลัมน์ — นักพัฒนาหลายคนตั้ง VARCHAR(255) เป็น Default โดยไม่คิด แม้ว่า VARCHAR ใช้พื้นที่ตามจริง แต่ค่า 255 ส่งผลต่อการจัดสรร Memory ชั่วคราวเมื่อทำ Sorting หรือ CREATE TEMPORARY TABLE กำหนดค่าให้เหมาะสม เช่น ชื่อใช้ VARCHAR(100) อีเมลใช้ VARCHAR(254)

ใช้ FLOAT เก็บเงิน — ปัญหาคลาสสิกที่เจอเมื่อยอดเงินคลาดเคลื่อนเศษสตางค์ ตัวอย่างเช่น 0.1 + 0.2 ใน FLOAT ไม่เท่ากับ 0.3 พอดี ระบบการเงินต้องใช้ DECIMAL เท่านั้น

ใช้ VARCHAR เก็บวันที่ — เก็บวันที่เป็น ‘2025-01-15’ ใน VARCHAR ทำให้ไม่สามารถใช้ Date Functions, เปรียบเทียบช่วงวัน หรือเรียงลำดับได้ถูกต้อง ให้ใช้ DATE หรือ DATETIME เสมอ

ใช้ INT เก็บ IP Address — แม้ IPv4 สามารถแปลงเป็น INT UNSIGNED ได้ด้วย INET_ATON() แต่ถ้าต้องรองรับ IPv6 ในอนาคต ควรใช้ VARBINARY(16) หรือ VARCHAR(45) แทน

ไม่ใส่ UNSIGNED ให้ Auto Increment — Primary Key แบบ Auto Increment ไม่มีวันติดลบ การไม่ใส่ UNSIGNED ทำให้เสียช่วงค่าบวกไปครึ่งหนึ่งโดยไม่จำเป็น

ตัวอย่างการออกแบบตาราง

ตัวอย่างตารางสินค้าที่เลือก Data Type ได้เหมาะสม

CREATE TABLE products (
    id          INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    sku         CHAR(12)       NOT NULL UNIQUE,
    name        VARCHAR(200)   NOT NULL,
    description TEXT,
    price       DECIMAL(10,2)  NOT NULL,
    weight_kg   DECIMAL(6,3),
    stock_qty   SMALLINT UNSIGNED NOT NULL DEFAULT 0,
    status      ENUM('active','inactive','discontinued') NOT NULL DEFAULT 'active',
    attributes  JSON,
    created_at  TIMESTAMP      NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at  TIMESTAMP      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

ในตัวอย่างนี้ id ใช้ INT UNSIGNED เพราะเป็น Auto Increment, sku ใช้ CHAR(12) เพราะมีความยาวคงที่, price ใช้ DECIMAL(10,2) เพราะเป็นเงิน, stock_qty ใช้ SMALLINT UNSIGNED เพราะจำนวนสินค้าไม่เกิน 65,535 ชิ้น, status ใช้ ENUM เพราะมีแค่ 3 ค่าตายตัว และ attributes ใช้ JSON เพราะแต่ละสินค้ามีคุณสมบัติต่างกัน

สรุป

การเลือก Data Type ที่ถูกต้องเป็นรากฐานของการออกแบบฐานข้อมูลที่ดี หลักการสำคัญคือเลือกขนาดเล็กที่สุดที่เพียงพอ ใช้ DECIMAL สำหรับข้อมูลการเงิน เลือก CHAR สำหรับข้อมูลความยาวคงที่ VARCHAR สำหรับข้อมูลทั่วไป และ TIMESTAMP สำหรับเวลาที่ระบบบันทึกอัตโนมัติ หลีกเลี่ยงข้อผิดพลาดที่พบบ่อย เช่น VARCHAR(255) ทุกคอลัมน์หรือ FLOAT สำหรับเงิน การลงทุนเวลาคิดเรื่องชนิดข้อมูลตั้งแต่ต้นจะช่วยประหยัดทั้งเวลาและทรัพยากรในระยะยาว

แนะนำบริการ DE

หากต้องการเซิร์ฟเวอร์สำหรับรันระบบจัดการฐานข้อมูลที่ปรับแต่ง Data Types, Index และ Configuration ได้อย่างเต็มที่ Cloud VPS ของ DE ให้ Root Access เต็มรูปแบบ พร้อม SSD Storage ที่ให้ IOPS สูง เหมาะกับ Database Workload ที่ต้องการ I/O เร็ว

สำหรับผู้ที่ต้องการโฮสต์เว็บไซต์พร้อมฐานข้อมูลที่ตั้งค่าให้เรียบร้อย Cloud Hosting ของ DE มาพร้อมระบบจัดการฐานข้อมูลและ phpMyAdmin เหมาะกับ WordPress และเว็บแอปพลิเคชันทั่วไป