1. 程式人生 > >資料庫中將數字型別儲存成字元型有哪些壞處?

資料庫中將數字型別儲存成字元型有哪些壞處?

    工作中經常見到一些設計粗糙的資料庫,其中將數字型別的欄位定義和儲存成字元型是一種比較常見的情況。部分開發同學或者非資料庫崗位很多為了圖方便,往往又不在意這些細節,除非等到出現可見的或者一些不可見的問題時才會去研究隱藏其後的真相。所以,這裡簡單總結一下,在資料庫系統中,將數字型別儲存成字元型別會有哪些不好的地方。以關係型資料庫MySQL示例。

    ①字元型別往往比數字型別佔用更多的儲存

    首先,字元型別往往佔用更多的儲存,但非總是佔用更多的儲存。MySQL以固定長度儲存數字型別:TINYINT、SMALLINT、MEDIUMINT、INT和BIGINT分別佔用1、2、3、4和8個Byte儲存。所以,就“1024”這個數值,如果以BIGINT、CHAR(4)和VARCHAR儲存,不考慮其他,分別需要8、4和5個Byte儲存(VARCHAR型別以是否儲存了超過255個字元為界限分別需要額外1個或者2個Byte來記錄儲存長度)。但實際使用中,我們遇到的數字型別,其值域或者說取值空間是很寬大的,或者剛好可以放入一個較小的數字型別定義中。絕大多數情況下,總是可以通過選用數字型別從而節省儲存空間。檔案型資料庫,資料最終是儲存在磁碟上的檔案,當需要對其查詢時再讀取到記憶體中。所以,更少的儲存空間消耗與更高的查詢效能是直接相關的。

    ②數字型別儲存成字元型別可能使索引失效

    欄位間進行比較時,如果欄位型別不一致,在查詢之前需要型別轉換,否則無法直接進行比較。因為額外的型別轉換操作,使得欄位上的索引不可用。因此,將數字型別儲存成字元型別,並且為其定義了索引,當其與其他數字型別欄位關聯查詢時,該索引就失效了。

    ③數字型別儲存成字元型別可能使部分SQL函式和命令失效

    數字的計算比較是作為一個整體在CPU中進行的。當數字型別儲存成字元型時,會受到編碼和排序規則影響;對字元型的查詢是從左往右一個字元一個字元依次進行的,即最左字首匹配。因此,將字元型數字直接傳入部分函式或命令會使結果失真。

    首先建立兩張示例表,插入相同的資料,但資料型別定義不一致。

CREATE TABLE `i` (
	`i` INT(11) NULL DEFAULT NULL
)
;
INSERT INTO `i` (`i`) VALUES (4);
INSERT INTO `i` (`i`) VALUES (8);
INSERT INTO `i` (`i`) VALUES (11);
INSERT INTO `i` (`i`) VALUES (23);
INSERT INTO `i` (`i`) VALUES (1024);
INSERT INTO `i` (`i`) VALUES (2147483647);
CREATE TABLE `s` (
	`s` VARCHAR(50) NULL DEFAULT NULL
)
;
INSERT INTO `s` (`s`) VALUES ('4');
INSERT INTO `s` (`s`) VALUES ('8');
INSERT INTO `s` (`s`) VALUES ('11');
INSERT INTO `s` (`s`) VALUES ('23');
INSERT INTO `s` (`s`) VALUES ('1024');
INSERT INTO `s` (`s`) VALUES ('2147483647');

    數字型別儲存成字元型使得SQL查詢結果失真。

mysql> SELECT MAX(i.i) FROM i;
+------------+
| MAX(i.i)   |
+------------+
| 2147483647 |
+------------+
1 row in set (0.00 sec)

mysql> SELECT MAX(s.s) FROM s;
+----------+
| MAX(s.s) |
+----------+
| 8        |
+----------+
1 row in set (0.00 sec)
mysql> SELECT i.i FROM i ORDER BY i.i;
+------------+
| i          |
+------------+
|          4 |
|          8 |
|         11 |
|         23 |
|       1024 |
| 2147483647 |
+------------+
6 rows in set (0.00 sec)

mysql> SELECT s.s FROM s ORDER BY s.s;
+------------+
| s          |
+------------+
| 1024       |
| 11         |
| 2147483647 |
| 23         |
| 4          |
| 8          |
+------------+
6 rows in set (0.00 sec)

    為了得到預想的數值結果,必須額外進行一次資料型別轉換操作,包括隱式轉換和顯式轉換兩種方式。隱式轉換是進行一個簡單的不改變數值大小的數學計算;顯式轉換是藉助CAST或者CONVERT函式。隱式操作在開發維護工作中往往就是出問題的根源,所以如果真的到了需要進行型別轉換的時候,建議使用標準SQL規範的CAST。

mysql> SELECT MAX(s.s + 0) FROM s;
+--------------+
| MAX(s.s + 0) |
+--------------+
|   2147483647 |
+--------------+
1 row in set (0.01 sec)

mysql> SELECT MAX(CAST(s.s AS DECIMAL)) FROM s;
+---------------------------+
| MAX(CAST(s.s AS DECIMAL)) |
+---------------------------+
|                2147483647 |
+---------------------------+
1 row in set (0.00 sec)

    參考: