Sửa lỗi MySQL ERROR 1364: Field doesn't have a default value khi INSERT

beginner🗄️ MySQL2026-05-31| MySQL 5.7+, MySQL 8.0 — Linux/macOS/Windows, mọi client (CLI, PHP, Python, Node.js)

Error Message

ERROR 1364 (HY000): Field 'column_name' doesn't have a default value
#mysql#sql-error#insert#strict-mode

Tình huống gặp phải

Bạn đang chạy một câu lệnh INSERT vốn hoạt động tốt trên máy dev cũ — hoặc phiên bản MySQL cũ hơn — nhưng bây giờ lại báo lỗi:

ERROR 1364 (HY000): Field 'created_by' doesn't have a default value

Câu truy vấn không thay đổi gì. Cột vẫn tồn tại. Vậy mà MySQL từ chối insert. Thủ phạm chính là strict SQL mode — và nó thường xuất hiện sau khi nâng cấp server hoặc chuyển môi trường.

Nguyên nhân

Từ MySQL 5.7 trở đi, STRICT_TRANS_TABLES đã được đưa vào sql_mode mặc định. Trước đó, nếu bỏ qua một cột NOT NULL không có giá trị mặc định, MySQL chỉ lặng lẽ lưu chuỗi rỗng hoặc số không. Khi strict mode được bật, MySQL sẽ từ chối insert thay vì làm vậy.

Cột gây ra lỗi thường có đặc điểm:

  • Được khai báo là NOT NULL
  • Không có giá trị DEFAULT
  • Không được đưa vào câu lệnh INSERT

Kiểm tra định nghĩa cột:

SHOW CREATE TABLE your_table\G

Nếu bạn thấy kết quả như sau:

`created_by` varchar(100) NOT NULL,

— không có DEFAULT và có NOT NULL — thì đó chính là nguyên nhân.

Cách sửa nhanh: thêm cột vào câu lệnh INSERT

Cách nhanh nhất: cung cấp giá trị còn thiếu trực tiếp.

-- Trước (bị lỗi)
INSERT INTO orders (product_id, quantity) VALUES (42, 3);

-- Sau (đã sửa)
INSERT INTO orders (product_id, quantity, created_by) VALUES (42, 3, 'system');

Nếu cột đó luôn nên có một giá trị mặc định hợp lý, hãy định nghĩa trong schema để bạn không phải nhớ mỗi lần viết truy vấn.

Sửa triệt để: thêm DEFAULT cho cột

Khi cột nên luôn có một giá trị dự phòng hợp lý, hãy bổ sung DEFAULT ngay vào schema:

ALTER TABLE orders
  MODIFY COLUMN created_by VARCHAR(100) NOT NULL DEFAULT 'system';

Các cột timestamp thường được xử lý theo cách này:

ALTER TABLE orders
  MODIFY COLUMN created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;

ALTER TABLE orders
  MODIFY COLUMN updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;

Sau khi ALTER, câu INSERT gốc của bạn sẽ chạy được mà không cần sửa một dòng truy vấn nào.

Cách khác: cho phép cột nhận giá trị NULL

Đôi khi "không có giá trị" là trạng thái hoàn toàn hợp lệ với cột đó. Hãy bỏ ràng buộc NOT NULL:

ALTER TABLE orders
  MODIFY COLUMN created_by VARCHAR(100) DEFAULT NULL;

Khi bỏ cột khỏi INSERT, MySQL sẽ lưu NULL một cách lặng lẽ — điều này hoàn toàn ổn với strict mode.

Giải pháp tạm thời: tắt strict mode cho session hiện tại

Đôi khi bạn cần câu INSERT đó phải chạy được ngay lập tức — ví dụ giữa quá trình migration, hoặc khi không thể ALTER bảng. Hãy tắt strict mode cho session hiện tại; các kết nối khác không bị ảnh hưởng:

SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', ''));

-- Chạy câu INSERT đang bị lỗi
INSERT INTO orders (product_id, quantity) VALUES (42, 3);

-- Khôi phục strict mode (hoặc chỉ cần đóng kết nối)
SET SESSION sql_mode = @@GLOBAL.sql_mode;

Đừng xem đây là cách sửa thực sự. Strict mode tồn tại để phát hiện các vấn đề dữ liệu trước khi chúng vào database. Tắt nó ở cấp global và MySQL sẽ bắt đầu lặng lẽ lưu chuỗi rỗng và số không vào các trường không nên để trống.

Kiểm tra và thay đổi sql_mode toàn cục

Xem trạng thái hiện tại:

SELECT @@GLOBAL.sql_mode;
SELECT @@SESSION.sql_mode;

Để thay đổi có hiệu lực vĩnh viễn cho tất cả kết nối, hãy chỉnh file cấu hình MySQL — thường là /etc/mysql/mysql.conf.d/mysqld.cnf hoặc /etc/my.cnf tùy bản phân phối:

[mysqld]
sql_mode = "ONLY_FULL_GROUP_BY,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION"

Sau đó khởi động lại:

sudo systemctl restart mysql

Việc bỏ STRICT_TRANS_TABLES là một sự đánh đổi thực sự. Bạn đang đổi tính an toàn dữ liệu lấy khả năng tương thích. Chỉ thực hiện hướng này sau khi kiểm tra kỹ xem các lần insert "lặng lẽ" đó thực sự sẽ lưu gì vào bảng của bạn.

Phía ứng dụng: gán giá trị mặc định trước khi INSERT

Code ứng dụng gặp lỗi này cần gán giá trị trước khi thực thi truy vấn — chứ không phải vá sau. Trong Python với SQLAlchemy:

from datetime import datetime

new_order = Order(
    product_id=42,
    quantity=3,
    created_by=current_user.username or 'system',
    created_at=datetime.utcnow()
)
db.session.add(new_order)
db.session.commit()

Trong PHP PDO:

$stmt = $pdo->prepare(
    "INSERT INTO orders (product_id, quantity, created_by) VALUES (?, ?, ?)"
);
$stmt->execute([42, 3, $user ?? 'system']);

Xác nhận đã sửa thành công

Chạy các lệnh sau để kiểm tra mọi thứ đã đúng chưa:

-- Chạy lại câu INSERT đang bị lỗi
INSERT INTO orders (product_id, quantity) VALUES (42, 3);

-- Kết quả mong đợi: Query OK, 1 row affected

-- Kiểm tra bản ghi vừa thêm
SELECT * FROM orders ORDER BY id DESC LIMIT 1;

Nếu bạn đã ALTER cột, kiểm tra xem DEFAULT mới đã xuất hiện trong schema chưa:

SHOW COLUMNS FROM orders LIKE 'created_by';
-- Field      | Type         | Null | Key | Default | Extra
-- created_by | varchar(100) | NO   |     | system  |

Tóm tắt nguyên nhân

  • MySQL 5.7+ bật strict mode theo mặc định — đây là nguyên nhân phổ biến nhất
  • Các cột được đánh dấu NOT NULL mà không có DEFAULT bắt buộc phải xuất hiện trong mọi câu INSERT
  • Cách sửa tốt nhất: thêm DEFAULT cho cột, hoặc đưa cột vào câu truy vấn một cách tường minh
  • Ghi đè sql_mode ở cấp session là biện pháp cuối cùng — không phải giải pháp lâu dài

Related Error Notes