Lỗi gặp phải
ERROR 1264 (22003): Out of range value for column 'age' at row 1
Lỗi này xảy ra khi một giá trị không nằm trong phạm vi cho phép của kiểu dữ liệu của cột. Strict mode trong MySQL — được bật mặc định từ phiên bản 5.7 — từ chối tự động cắt bớt giá trị và ném ra lỗi này thay vì âm thầm chấp nhận dữ liệu sai.
Trường hợp điển hình: cột age của bạn có kiểu TINYINT (tối đa 127), nhưng ai đó gửi vào giá trị 300. Một trường hợp phổ biến khác: cột đếm kiểu INT nhận một giá trị được tính bằng cách nhân hai số lớn, vượt quá giới hạn 2.147.483.647.
Bước 1 — Xác nhận kiểu dữ liệu của cột
Kiểm tra kiểu dữ liệu thực tế của cột đang gặp lỗi:
DESCRIBE users;
-- hoặc xem chi tiết hơn:
SHOW COLUMNS FROM users LIKE 'age';
Kết quả trả về sẽ có dạng:
+-------+---------+------+-----+---------+
| Field | Type | Null | Key | Default |
+-------+---------+------+-----+---------+
| age | tinyint | YES | | NULL |
+-------+---------+------+-----+---------+
Từ đây bạn biết được giới hạn tối đa. Dưới đây là phạm vi giá trị của các kiểu số nguyên có dấu:
TINYINT : -128 to 127
SMALLINT : -32,768 to 32,767
MEDIUMINT : -8,388,608 to 8,388,607
INT : -2,147,483,648 to 2,147,483,647
BIGINT : -9.2 × 10^18 to 9.2 × 10^18
Các biến thể UNSIGNED sẽ tăng gấp đôi giới hạn dương — TINYINT UNSIGNED cho phép giá trị từ 0–255, SMALLINT UNSIGNED từ 0–65.535.
Bước 2 — Chọn cách khắc phục
Có ba hướng xử lý. Cách phù hợp phụ thuộc vào việc kiểu dữ liệu của cột quá hẹp, dữ liệu đầu vào sai, hay cả hai.
Cách A — Mở rộng kiểu dữ liệu của cột
Khi các giá trị hợp lệ thực sự không vừa, hãy thay đổi kiểu cột:
-- age không thể vượt 127 với TINYINT — chuyển sang SMALLINT UNSIGNED
ALTER TABLE users MODIFY COLUMN age SMALLINT UNSIGNED;
-- bộ đếm lượt xem chạm giới hạn INT ở 2.1 tỷ — chuyển sang BIGINT
ALTER TABLE events MODIFY COLUMN view_count BIGINT UNSIGNED NOT NULL DEFAULT 0;
Chạy lại câu lệnh INSERT đang bị lỗi sau khi ALTER. Lần này sẽ thành công.
Cách B — Kiểm tra và sửa dữ liệu đầu vào
Dữ liệu đầu vào sai? Hãy xử lý từ nguồn. Thêm ràng buộc CHECK trong MySQL để kiểm tra ngay tại tầng cơ sở dữ liệu:
-- MySQL 8.0.16+ hỗ trợ CHECK constraints
ALTER TABLE users ADD CONSTRAINT chk_age CHECK (age BETWEEN 0 AND 120);
-- Chèn giá trị 300 giờ sẽ báo lỗi ràng buộc rõ ràng
INSERT INTO users (name, age) VALUES ('Alice', 300);
-- ERROR 3819 (HY000): Check constraint 'chk_age' is violated.
Hoặc kiểm tra trong ứng dụng trước khi thực hiện INSERT:
# Ví dụ Python
if not (0 <= age <= 120):
raise ValueError(f"Age {age} is out of valid range")
Cách C — Dùng UNSIGNED khi giá trị âm là không thể xảy ra
Các cột lưu trữ ID, số đếm, hoặc kích thước sẽ không bao giờ chứa số âm. Hãy khai báo rõ điều đó — và tăng gấp đôi phạm vi lưu trữ mà không tốn thêm chi phí:
ALTER TABLE orders MODIFY COLUMN quantity SMALLINT UNSIGNED NOT NULL DEFAULT 0;
-- SMALLINT UNSIGNED: 0 đến 65.535 (so với giới hạn có dấu là 32.767)
Còn Strict Mode thì sao?
Các máy chủ MySQL 5.6 cũ hơn — hoặc bất kỳ instance nào có strict mode bị tắt — sẽ âm thầm cắt giá trị xuống mức tối đa hoặc tối thiểu của kiểu dữ liệu thay vì báo lỗi. Kiểm tra chế độ hiện tại:
SELECT @@sql_mode;
Nếu thấy STRICT_TRANS_TABLES hoặc STRICT_ALL_TABLES trong kết quả, nghĩa là strict mode đang được bật. Đây là hành vi đúng. Lỗi này thực ra là một tính năng — MySQL từ chối âm thầm làm hỏng dữ liệu của bạn.
Đừng tắt strict mode để tránh lỗi. Bạn sẽ chỉ lưu dữ liệu sai một cách âm thầm, điều đó còn tệ hơn nhiều so với một lỗi hiện ra rõ ràng.
Tò mò MySQL sẽ cắt về giá trị nào? Thử kiểm tra tạm thời:
SET sql_mode = '';
INSERT INTO users (name, age) VALUES ('test', 300);
-- Chèn giá trị 127 (max của TINYINT) — không báo lỗi, nhưng dữ liệu sai
SELECT age FROM users WHERE name = 'test';
-- Trả về 127
Bật lại strict mode ngay sau khi kiểm tra xong:
SET sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';
Kiểm tra lại sau khi sửa
Sau khi ALTER, xác nhận kiểu dữ liệu đã thay đổi thực sự:
SHOW COLUMNS FROM users LIKE 'age';
-- Kết quả phải hiển thị SMALLINT UNSIGNED
Sau đó chạy lại câu lệnh INSERT trước đó đang bị lỗi:
INSERT INTO users (name, age) VALUES ('Bob', 300);
Query OK, 1 row affected (0.01 sec)
SELECT age FROM users WHERE name = 'Bob';
-- Trả về 300 ✓
Mẹo để tránh lỗi này trong tương lai
- Chọn kiểu dữ liệu phù hợp với miền giá trị thực tế, không chỉ dựa vào dữ liệu hiện tại. Cột
agekhông cần BIGINT, nhưng TINYINT thì quá chật — SMALLINT UNSIGNED mới là lựa chọn đúng. - Dùng BIGINT cho ID và các cột auto-increment trên các bảng có lưu lượng cao. Giới hạn 2.1 tỷ của INT có thể đạt tới nhanh hơn bạn nghĩ.
- Thêm CHECK constraints (MySQL 8.0.16+) cho các giới hạn nghiệp vụ. Chúng áp dụng quy tắc ở tầng cơ sở dữ liệu bất kể ứng dụng nào ghi vào, và thông báo lỗi rất rõ ràng.
- Kiểm tra các giá trị biên trong quá trình phát triển. Hãy chèn thử giá trị tối thiểu và tối đa của kiểu dữ liệu trước khi triển khai — phát hiện lỗi biên trong môi trường dev rẻ hơn nhiều so với lúc lên production.
- Giữ strict mode luôn bật. Việc dữ liệu bị cắt bớt âm thầm là loại lỗi có thể mất hàng tuần để chẩn đoán. Lỗi hiện ra ngay là đang giúp bạn đó.

