MySQL系列:隱式類型轉化可能帶來的坑


在開發規范中,我們往往會要求研發避免在where條件中出現隱式類型轉換,這么要求大概有以下兩方面的原因:

  1. 隱式類型轉換可能導致索引失效;
  2. 隱式類型轉換可能產生非預期的結果。

注:這里說的是隱式類型轉換。

我們可以看下官方關於類型轉換的解釋:

這里討論以下情況:

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

 


免責聲明!

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



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