mysql版本:5.7.24
一、問題描述
創建表執行sql語句如下:
create table train_record ( id int(11) not NULL AUTO_INCREMENT, user_name VARCHAR(20) NOT NULL COMMENT'用戶名稱', train_name TINYINT(1) DEFAULT'2' NOT NULL COMMENT'訓練項目,1主動訓練 2引導訓練', start_time TIMESTAMP NOT NULL COMMENT'開始時間', end_time TIMESTAMP NOT NULL COMMENT'結束時間', train_duration INT(4) NOT NULL COMMENT'訓練時長', train_times int(4) DEFAULT'1' NOT NULL COMMENT'訓練次數,1-16次 2-32次 3-48次 4-64次 5-80次 6-96次', repeat_time TINYINT(1) DEFAULT'1' NOT NULL COMMENT'點擊后重復播放次數,1-1次 2-2次 3-3次', volume int(3) DEFAULT'0' NOT NULL COMMENT'音量', play_random TINYINT(1) DEFAULT'1' NOT NULL COMMENT'是否隨機播放,1是0否', PRIMARY KEY(id) ) COMMENT'訓練記錄表';
執行sql報錯結果:
1067 - Invalid default value for 'end_time', Time: 0.000000s
原因:mysql從5.7開始,默認是嚴格模式,嚴格遵從SQL92規范。
mysql> show variables like 'explicit_defaults_for_timestamp';
執行結果:變量explicit_defaults_for_timestamp的value值為off。
mysql> show variables like 'sql_mode';
執行結果如下:
ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,
ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
1. 親測,創建表如果只有一個字段設為TIMESTAMP類型,默認情況下,沒有指明null屬性,該字段就會自動加上NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP屬性,如果給該字段插入null值,會自動給該字段設置為CURRENT_TIMESTAMP(當前時間)值。
說明:DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP表示:在創建新記錄和修改現有記錄的時候都對這個數據列刷新。
2. 親測,創建表如果有一個以上的字段指定TIMESTAMP類型,如果沒有指定null屬性或者沒有設置默認值,則第二個TIMESTAMP字段報Invalid錯誤。
> 這是因為:
第一個TIMESTAMP字段會自動被加上DEFAULT CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAMP屬性。
第一個TIMESTAMP之后的字段,會被自動加上DEFAULT ‘0000-00-00 00:00:00’屬性。而5.7版本的sql_mode變量中含有NO_ZERO_DATE,表示'0000-00-00 00:00:00'格式非法,這與嚴格模式有關。
二、解決辦法
修改全局變量explicit_defaults_for_timestamp
mysql> set global explicit_defaults_for_timestamp = ON;
說明:需要退出重新進入mysql,該變量才生效。
此時,執行創建表sql語句,親測執行成功。
1. 由於沒有指定默認值,假設start_time和end_time值為空,執行insert操作,受嚴格模式影響,執行語句報錯,報錯語句如下:
1364 - Field 'start_time' doesn't have a default value, Time: 0.000000s
如果將值設為0000-00-00 00:00:00,執行insert操作,報錯如下:
1292 - Incorrect datetime value: '0000-00-00 00:00:00' for column 'start_time' at row 1, Time: 0.001000s
這時候就需要修改全局變量sql_mode,去掉NO_ZERO_IN_DATE,NO_ZERO_DATE,執行sql語句如下:
mysql> set global sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
再執行insert操作,親測成功。
insert into train_record (user_name, train_name, start_time, end_time, train_duration, train_times, repeat_time, volume, play_random) VALUES ( liulin', 2, '0000-00-00 00:00:00', '0000-00-00 00:00:00', 30, 4, 3, 50, 1);
因此可以在創建sql語句時,將start_time和end_time字段的not null屬性后面加上default '0000-00-00 00:00:00',如下所示:
start_time timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '創建時間', end_time timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '創建時間',
這樣當TIMESTAMP字段值為空時,默認插入0000-00-00 00:00:00,執行語句不會再報錯。
2. 如果將start_time和end_time這兩個字段的not null去掉,執行sql語句,兩個字段會被自動加上DEFAULT DULL的屬性,執行insert操作的時候,若TIMESTAMP字段值為空,則記錄為null,不再是CURRENT_TIMESTAMP(當前時間)值。
三、總結
1. 之前公司項目使用MySQL版本為5.6.42,在備份遷移到本地數據庫就遇到了日期0000-00-00 00:00:00格式非法的問題,因為是導入SQL文件,批量替換有點麻煩,所以只要將sql_mode的NO_ZERO_IN_DATE, NO_ZERO_DATE去掉即可。
2. 關於explicit_default_for_timestamp的解釋可參考mysql官網的 Server System Variables 一文 ,右上角可切換版本查看不同mysql版本的系統變量介紹。
3. 業務需要精確到秒時,也可以考慮DateTime類型。
