Data truncation: Out of range value for column 'quanity' at row 問題解決方案


由於之前在自己電腦上搭建了mysql 5.6的數據庫,但是在服務器上搭建的是mysql 5.7的環境,在運行過程中出現了如下錯誤:

Data truncation: Out of range value for column 'quanity' at row

1. 解決問題

  1. 問題索源

由於安裝的時候沒有將STRICT_TRANS_TABLES關閉導致的

  1. 排查問題
show variables like "sql_mode";

發現其中有STRICT_TRANS_TABLES

  1. 解決問題

將該字段去除即可

set sql_mode='NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';

2. sql_mode 常用值說明

官方手冊專門有一節介紹 https://dev.mysql.com/doc/refman/5.6/en/sql-mode.html 。 SQL Mode 定義了兩個方面:MySQL應支持的SQL語法,以及應該在數據上執行何種確認檢查。

  • SQL語法支持類

    • ONLY_FULL_GROUP_BY
      對於GROUP BY聚合操作,如果在SELECT中的列、HAVING或者ORDER BY子句的列,沒有在GROUP BY中出現,那么這個SQL是不合法的。是可以理解的,因為不在 group by 的列查出來展示會有矛盾。
      在5.7中默認啟用,所以在實施5.6升級到5.7的過程需要注意:

       Expression #1 of SELECT list is not in GROUP BY
      clause and contains nonaggregated column
      '1066export.ebay_order_items.TransactionID' which
      is not functionally dependent on columns in GROUP BY
      clause; this is incompatible with sql_mode=only_full_group_by
      
    • ANSI_QUOTES
      啟用 ANSI_QUOTES 后,不能用雙引號來引用字符串,因為它被解釋為識別符,作用與 一樣。 設置它以后,update t set f1="" ...`,會報 Unknown column ‘’ in ‘field list 這樣的語法錯誤。

    • PIPES_AS_CONCAT
      || 視為字符串的連接操作符而非 或 運算符,這和Oracle數據庫是一樣的,也和字符串的拼接函數 CONCAT() 相類似

    • NO_TABLE_OPTIONS
      使用 SHOW CREATE TABLE 時不會輸出MySQL特有的語法部分,如 ENGINE ,這個在使用 mysqldump 跨DB種類遷移的時候需要考慮。

    • NO_AUTO_CREATE_USER
      字面意思不自動創建用戶。在給MySQL用戶授權時,我們習慣使用 GRANT ... ON ... TO dbuser 順道一起創建用戶。設置該選項后就與oracle操作類似,授權之前必須先建立用戶。5.7.7開始也默認了。

  • 數據檢查類

    • NO_ZERO_DATE
      認為日期 ‘0000-00-00’ 非法,與是否設置后面的嚴格模式有關。
      1.如果設置了嚴格模式,則 NO_ZERO_DATE 自然滿足。但如果是 INSERT IGNORE 或 UPDATE IGNORE,’0000-00-00’依然允許且只顯示warning
      2.如果在非嚴格模式下,設置了NO_ZERO_DATE,效果與上面一樣,’0000-00-00’允許但顯示warning;如果沒有設置NO_ZERO_DATE,no warning,當做完全合法的值。
      3.NO_ZERO_IN_DATE情況與上面類似,不同的是控制日期和天,是否可為 0 ,即 2010-01-00 是否合法。
    • NO_ENGINE_SUBSTITUTION
      使用 ALTER TABLECREATE TABLE 指定 ENGINE 時, 需要的存儲引擎被禁用或未編譯,該如何處理。啟用NO_ENGINE_SUBSTITUTION時,那么直接拋出錯誤;不設置此值時,CREATE用默認的存儲引擎替代,ATLER不進行更改,並拋出一個 warning .
    • STRICT_TRANS_TABLES
      設置它,表示啟用嚴格模式。
      注意 STRICT_TRANS_TABLES 不是幾種策略的組合,單獨指 INSERTUPDATE出現少值或無效值該如何處理:
      1.前面提到的把 ‘’ 傳給int,嚴格模式下非法,若啟用非嚴格模式則變成0,產生一個warning
      2.Out Of Range,變成插入最大邊界值
      3.A value is missing when a new row to be inserted does not contain a value for a non-NULL column that has no explicit DEFAULT clause in its definition

上面並沒有囊括所有的 SQL Mode,選了幾個代表性的,詳細還是 看手冊

sql_mode一般來說很少去關注它,沒有遇到實際問題之前不會去啟停上面的條目。我們常設置的 sql_mode 是 ANSISTRICT_TRANS_TABLESTRADITIONAL,ansi和traditional是上面的幾種組合。

  • ANSI:更改語法和行為,使其更符合標准SQL
    相當於REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE
  • TRADITIONAL:更像傳統SQL數據庫系統,該模式的簡單描述是當在列中插入不正確的值時“給出錯誤而不是警告”。
    相當於 STRICT_TRANS_TABLES, STRICT_ALL_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, NO_AUTO_CREATE_USER, NO_ENGINE_SUBSTITUTION
  • ORACLE:相當於 PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, NO_KEY_OPTIONS, NO_TABLE_OPTIONS, NO_FIELD_OPTIONS, NO_AUTO_CREATE_USER

無論何種mode,產生error之后就意味着單條sql執行失敗,對於支持事務的表,則導致當前事務回滾;但如果沒有放在事務中執行,或者不支持事務的存儲引擎表,則可能導致數據不一致。MySQL認為,相比直接報錯終止,數據不一致問題更嚴重。於是 STRICT_TRANS_TABLES 對非事務表依然盡可能的讓寫入繼續,比如給個”最合理”的默認值或截斷。而對於 STRICT_ALL_TABLES,如果是單條更新,則不影響,但如果更新的是多條,第一條成功,后面失敗則會出現部分更新。

5.6.6 以后版本默認就是NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES,5.5默認為 ‘’ 。

3. 設置 sql_mode

查看

查看當前連接會話的sql模式:

mysql> select @@session.sql_mode;
或者從環境變量里取
mysql> show variables like "sql_mode";


查看全局sql_mode設置:
mysql> select @@global.sql_mode;

只設置global,需要重新連接進來才會生效

設置

形式如
mysql> set sql_mode='';
mysql> set global sql_mode='NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES';


如果是自定義的模式組合,可以像下面這樣

Adding only one mode to sql_mode without removing existing ones:
mysql> SET sql_mode=(SELECT CONCAT(@@sql_mode,',<mode_to_add>'));

Removing only a specific mode from sql_mode without removing others:
mysql> SET sql_mode=(SELECT REPLACE(@@sql_mode,'<mode_to_remove>',''));

配置文件里面設置sql-mode=""

一個有趣的試驗

updated: 2017-12-10

現網做數據遷移測試時報另一個錯誤,原由是這樣的:一個1.8億的表里面,因為某種原因需要把字段定義null改為not null,避免下游服務(如ES)處理特殊數據時異常情況。但這是一個並發dml非常高又達到100多G的大表,online ddl針對這種修改字段類型簡直束手無策,pt-osc也慢的很。

剛好有一個做數據遷移的契機,原本打算在新庫上把字段改好,再通過dts或類似的數據遷移工具,同步過去。在非嚴格模式下,原本是null的值也會變成0或’’,但還是報錯了:

set sql_mode='';  -- 置為非嚴格模式
insert into t(id, a) values(1, null);
[Err] 1048 - Column 'a' cannot be null

然而
insert into t(id, a) values(1, null),(2, null),;
Affected rows: 2

找到官方文檔上的原話,可以解釋:

If you are not using strict mode, then whenever you insert an “incorrect” value into a column, such as a NULL into a NOT NULL column or a too-large numeric value into a numeric column, MySQL sets the column to the “best possible value” instead of producing an error

If you try to store NULL into a column that doesn’t take NULL values, an error occurs for single-row INSERT statements. For multiple-row INSERT statements or for INSERT INTO … SELECT statements, MySQL Server stores the implicit default value for the column data type

非嚴格模式下,單行插入 null 到 not null 列,會失敗;多行插入則只是warning。規則是這樣,也就無需解釋。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM