mysql儲存資料,varchar型別中的資料變成了科學計數法?

鐵柱同學發表於2020-09-24

一、前言

      這個問題也是比較奇怪的,明明設定的是varchar型別,但儲存的結果卻是科學計數法,這還了得,必須找一下原因了

1、表現形式

            [uuid] => 1460444056320623751784
            [log_time] => 2016-05-30 01:03:01
            [daily_nums] => 1.125899906842624e15

      就是這個daily_nums欄位,十分的奇怪。

2、出現錯誤的sql

update test set daily_nums_nums =pow(2,x) where 1

      此處的pow函式是求冪次方的,博主這裡是求2的n次方。,對了,mysql版本是5.7

二、問題排查

1、資料表結構

       $sql = "CREATE TEMPORARY TABLE if not exists test (
            uuid varchar(32) NOT NULL PRIMARY KEY,
            `log_time` datetime NOT NULL ,
            `daily_nums` VARCHAR(100) NOT NULL DEFAULT '0',
            UNIQUE KEY `uuid` (`uuid`),
            KEY (log_time)
        ) DEFAULT CHARSET utf8 COLLATE utf8_general_ci";

      這裡給出的是varchar(100)型別,沒道理存不下小小十幾位的資料才對,怪哉怪哉

2、錯誤推測

明明是varchar結果,為什麼反而成大數了了呢,推測是兩個可能

(1)資料庫讀出來是科學計數法
(2)資料庫裡面儲存的就是科學計數法

答案是第二種

3、最終原因

      此處應該是由於pow()這個函式的原因導致的,當2^50的時候,已經達到了15位,當51次方的時候,超過了15位,就記成了科學技術法

	2^ 50  = 562949953421312
	2^ 51 =  1.125899906842624e15

      猜測也是有內部的資料結構導致的,類似於浮點型的精度問題,但是沒找到具體的答案,這裡不再深究。

      參考php14位大數轉化為科學計數法:php使用位運算來實現日留存的演算法

三、解決方案

1、更改資料結構

更改欄位為bigint即可,下面可以測試下,當插入科學計數法的時候,bigintvarchar的區別:

(1)表結構

		CREATE TABLE `temp_drr_test` (
  `uuid` varchar(32) NOT NULL,
  `daily_nums` bigint(20) NOT NULL DEFAULT '0',
  `weekly_nums` bigint(20) NOT NULL DEFAULT '0',
  `monthly_nums` bigint(20) NOT NULL DEFAULT '0',
  `test_nums` varchar(100) DEFAULT '',
  UNIQUE KEY `uuid` (`uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
daily_nums為bigint, test_nums為varchar

(2)分別更新兩個欄位為 科學計數法 :2.251799813685248e15

	mysql> select * from temp_drr_test;
+------+---------------------+---------------------+--------------+---------------------+
| uuid | daily_nums          | weekly_nums         | monthly_nums | test_nums           |
+------+---------------------+---------------------+--------------+---------------------+
| 1111 | 4611686018427387904 | 9223372036854775807 |            0 | 4611686018427387904 |
+------+---------------------+---------------------+--------------+---------------------+
1 row in set (0.00 sec)

mysql> update temp_drr_test set daily_nums=2.251799813685248e15;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from temp_drr_test;
+------+------------------+---------------------+--------------+---------------------+
| uuid | daily_nums       | weekly_nums         | monthly_nums | test_nums           |
+------+------------------+---------------------+--------------+---------------------+
| 1111 | 2251799813685248 | 9223372036854775807 |            0 | 4611686018427387904 |
+------+------------------+---------------------+--------------+---------------------+
1 row in set (0.00 sec)

mysql> update temp_drr_test set test_nums=2.251799813685248e15;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from temp_drr_test;
+------+------------------+---------------------+--------------+----------------------+
| uuid | daily_nums       | weekly_nums         | monthly_nums | test_nums            |
+------+------------------+---------------------+--------------+----------------------+
| 1111 | 2251799813685248 | 9223372036854775807 |            0 | 2.251799813685248e15 |
+------+------------------+---------------------+--------------+----------------------+
1 row in set (0.00 sec)

      最終發現,當欄位為bigint的時候,是可以自動調節為數字的。只不過bigint能儲存的長度也有限,難保不會超過,所以用下一個方法

2、按位或上一個值

update test set daily_nums =pow(2,x) | 0 

      這裡選擇0,這樣在保證值不變的情況下,也能正常儲存了,經測試無誤,可以儲存為大數。

end

相關文章