在開發規范中,我們往往會要求研發避免在where條件中出現隱式類型轉換,這么要求大概有以下兩方面的原因:
- 隱式類型轉換可能導致索引失效;
- 隱式類型轉換可能產生非預期的結果。
注:這里說的是隱式類型轉換。
我們可以看下官方關於類型轉換的解釋:
這里討論以下情況:
In all other cases, the arguments are compared as floating-point (real) numbers.
回顧:一次研發提供過來的數據修正的sql:
1 update tbjxxxxaccout set balancexxx=balancexxx+0.1 where bankxxx=6222000233022332111;
即,根據銀行電子賬戶更新賬戶余額信息。執行后發現更新了多條數據(電子賬戶是唯一的),然后回滾操作;重新檢查sql,發現bankxxx的條件忘記加引號,正確的sql應該是:
1 update tbjxxxxaccout set balancexxx=balancexxx+0.1 where bankxxx='6222000233022332111';
bankxxx的字段類型為varchar(20),在沒有加引號的情況下發生了隱式數據類型轉換,按官方的解釋:這種情況的比較會轉換為浮點型數據再去比較,即6222000233022332111會轉化為string類型再轉化為float類型來比較(注:至於為什么會先轉換成string再轉換成float,這其中內部機制我也沒搞明白,有待考證)。
下面造幾個數據做測試:
1 CREATE TABLE `t_zw1` ( 2 3 `id` int(11) DEFAULT NULL, 4 5 `account_id` char(19) DEFAULT NULL, 6 7 `balance_amount` decimal(18,2) DEFAULT NULL 8 9 ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 10 11 12 13 insert into t_zw1 values(1, '6222000233022332111', 18.9); 14 15 insert into t_zw1 values(2, '6222000233022332211', 108.1); 16 17 insert into t_zw1 values(3, '6222000233022332311', 180.3); 18 19 insert into t_zw1 values(4, '6222000233022334211', 2.3); 20 21 insert into t_zw1 values(5, '6222000233022334311', 33.3); 22 23 24 25 root@zow 10:09:34>select * from t_zw1; 26 27 +------+---------------------+----------------+ 28 29 | id | account_id | balance_amount | 30 31 +------+------------------------------+----------------+ 32 33 | 1 | 6222000233022332111 | 18.90 | 34 35 | 2 | 6222000233022332211 | 108.10 | 36 37 | 3 | 6222000233022332311 | 180.30 | 38 39 | 4 | 6222000233022334211 | 2.30 | 40 41 | 5 | 6222000233022334311 | 33.30 | 42 43 +------+---------------------+------------------------+
我們看下account為6222000233022332111 的情況:
1 root@zow 10:17:02>select * from t_zw1 where account_id='6222000233022332111'; 2 3 +------+---------------------+----------------+ 4 5 | id | account_id | balance_amount | 6 7 +------+---------------------+----------------+ 8 9 | 1 | 6222000233022332111 | 18.90 | 10 11 +------+---------------------+----------------+ 12 13 1 row in set (0.00 sec) 14 15 16 17 root@zow 10:17:45>select * from t_zw1 where account_id=6222000233022332111; 18 19 +------+---------------------+----------------+ 20 21 | id | account_id | balance_amount | 22 23 +------+---------------------+----------------+ 24 25 | 1 | 6222000233022332111 | 18.90 | 26 27 | 2 | 6222000233022332211 | 108.10 | 28 29 | 3 | 6222000233022332311 | 180.30 | 30 31 +------+---------------------+----------------+ 32 33 3 rows in set (0.00 sec) 34 35 36 37 root@zow 10:18:59>select account_id, account_id+0.0, if(6222000233022332111=account_id, 1, 0) from t_zw1; 38 39 +---------------------+----------------------+------------------------------------------+ 40 41 | account_id | account_id+0.0 | if(6222000233022332111=account_id, 1, 0) | 42 43 +---------------------+----------------------+------------------------------------------+ 44 45 | 6222000233022332111 | 6.222000233022332e18 | 1 | 46 47 | 6222000233022332211 | 6.222000233022332e18 | 1 | 48 49 | 6222000233022332311 | 6.222000233022332e18 | 1 | 50 51 | 6222000233022334211 | 6.222000233022334e18 | 0 | 52 53 | 6222000233022334311 | 6.222000233022334e18 | 0 | 54 55 +---------------------+----------------------+------------------------------------------+
可以看出轉化為float類型后,由於浮點類型精度的問題,紅字的三條件記錄轉換后的值(約數)是一樣的。
總結:隱式類型轉換會帶了很多不確定的結果,一定要杜絕where條件中隱式轉換。
參考:https://dev.mysql.com/doc/refman/5.7/en/type-conversion.html