Tại sao quá trình Migration của bạn thất bạiCó thể bạn đã gặp phải trở ngại này khi di chuyển (migrate) một cơ sở dữ liệu cũ sang MySQL 5.7 hoặc 8.0. Lỗi này xảy ra khi một ứng dụng cố gắng đẩy một giá trị ngày "giả" như 0000-00-00 vào cột DATETIME. Các phiên bản MySQL cũ thường lỏng lẻo, nhưng các phiên bản hiện đại mặc định thực thi tính toàn vẹn dữ liệu nghiêm ngặt.
Các nguyên nhân phổ biến gây ra lỗi này bao gồm:
- Khôi phục bản sao lưu
.sqlcũ bằng lệnhmysql < backup.sql.- Chạy migration của Laravel hoặc Django trên một máy chủ mới được thiết lập.- Các ứng dụng PHP cũ sử dụng0000-00-00làm giá trị tạm thời thay vì cho phépNULL.``` ERROR 1292 (22007): Incorrect datetime value: '0000-00-00 00:00:00' for column 'created_at' at row 1
## Nguyên nhân gốc rễ: Strict ModeCác môi trường MySQL hiện đại sử dụng một thiết lập gọi là `sql_mode` để ngăn chặn việc hỏng dữ liệu. Có hai cờ (flag) cụ thể gây ra vấn đề về ngày tháng này. `NO_ZERO_IN_DATE` ngăn chặn các ngày như 2023-00-01, trong khi `NO_ZERO_DATE` cấm hoàn toàn giá trị `0000-00-00`.
Các cờ này thường được gộp chung trong `STRICT_TRANS_TABLES`. Chế độ này đảm bảo MySQL không âm thầm cắt bỏ dữ liệu không hợp lệ. Thay vào đó, nó dừng hoàn toàn thao tác để giữ cho dữ liệu của bạn luôn sạch.
## Bước 1: Kiểm tra SQL Mode của bạnTrước khi thay đổi bất kỳ cài đặt nào, hãy xem các chế độ nào đang thực sự hoạt động. Chạy lệnh này trong terminal MySQL của bạn:
SELECT @@sql_mode;
Bạn có thể sẽ thấy một chuỗi dài như `ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE...`. Nếu các cờ 'ZERO' này nằm trong danh sách, MySQL sẽ từ chối các giá trị ngày cũ của bạn mọi lúc.
## Bước 2: Khắc phục nhanh cho SessionĐôi khi bạn chỉ cần hoàn tất quá trình migration mà không muốn khởi động lại toàn bộ máy chủ. Bạn có thể tắt strict mode chỉ cho kết nối hiện tại. Cách này hoàn hảo cho việc import dữ liệu một lần.
-- Xóa sạch mode chỉ cho session này SET SESSION sql_mode = '';
-- Hoặc loại bỏ cụ thể các hạn chế zero-date SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode, 'NO_ZERO_DATE', '')); SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode, 'NO_ZERO_IN_DATE', ''));
Sau khi chạy các lệnh này, bạn có thể thực hiện các câu lệnh `INSERT` hoặc `UPDATE` trong cùng cửa sổ đó mà không gặp lỗi.
## Bước 3: Thực hiện thay đổi vĩnh viễnĐể duy trì cài đặt này sau khi khởi động lại máy chủ, bạn phải chỉnh sửa tệp cấu hình. Tệp này thường là `my.cnf` trên Linux hoặc `my.ini` trên Windows.
### Trên Linux (Ubuntu/Debian)- Mở tệp: `sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf`.- Tìm phần `[mysqld]`.- Thêm hoặc cập nhật dòng `sql_mode` để loại bỏ các cờ zero-date:```
[mysqld]
sql_mode = "STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION"
```- Khởi động lại dịch vụ: `sudo systemctl restart mysql`.### Trên Windows (XAMPP/WAMP)- Mở XAMPP Control Panel và nhấn vào "Config" cạnh MySQL.- Chọn `my.ini` và tìm kiếm chuỗi `sql_mode`.- Xóa `NO_ZERO_IN_DATE` và `NO_ZERO_DATE` khỏi dòng đó.- Lưu tệp và khởi động lại MySQL thông qua Control Panel.## Bước 4: Cách làm "Chuẩn" (Làm sạch dữ liệu)Tắt strict mode chỉ là giải pháp tạm thời, nhưng đó không phải là cách làm tốt nhất. Các ứng dụng hiện đại nên sử dụng `NULL` cho các ngày chưa xác định. Điều này giúp cơ sở dữ liệu của bạn tương thích với PostgreSQL và các engine khác vốn không bao giờ cho phép zero-date.
Đầu tiên, hãy sửa đổi bảng của bạn để cho phép giá trị `NULL`:
ALTER TABLE users MODIFY created_at DATETIME NULL;
Tiếp theo, chuyển đổi các chuỗi không hợp lệ đó thành giá trị `NULL` thực tế. Bạn có thể cần sử dụng tạm thời giải pháp Session Fix ở Bước 2 để chạy câu lệnh cập nhật này:
SET SESSION sql_mode = ''; UPDATE users SET created_at = NULL WHERE created_at = '0000-00-00 00:00:00';
## Xác minh và Mẹo chuyên giaXác nhận việc khắc phục bằng cách thử chèn dữ liệu giả. Nếu lệnh `INSERT INTO your_table (created_at) VALUES ('0000-00-00 00:00:00');` hoạt động, bạn đã vượt qua hạn chế thành công. Kiểm tra trạng thái global bất cứ lúc nào với `SELECT @@GLOBAL.sql_mode;`.
Khi xử lý dữ liệu cũ lộn xộn, bạn rất dễ bị lạc trong các định dạng ngày tháng kỳ lạ. Tôi thường sử dụng [Timestamp Converter trên ToolCraft](https://toolcraft.app/en/tools/datetime/timestamp-converter) để kiểm tra xem các số nguyên trong log có phải là Unix timestamp hợp lệ hay không. Nó giúp xác định xem một giá trị thực sự là 0 hay chỉ là một chuỗi định dạng sai trước khi bạn chạy một truy vấn `UPDATE` lớn.

