8. 選擇合適的資料型別
8.1 CHAR與VARCHAR
CHAR 和 VARCHAR 型別類似,都用來儲存字串,但它們儲存和檢索的方式不同。CHAR 屬於固定長度的字元型別,而 VARCHAR 屬於可變長度的字元型別。當char定義一定寬度的格式時,資料末尾的空格會被處理掉。char是固定長度的,處理速度比varchar塊,但缺點是浪費空間。
char和varchar如何選擇?
在 MySQL 中,不同的儲存引擎對 CHAR 和 VARCHAR 的使用原則有所不同,這裡簡單概括如下。
- MyISAM 儲存引擎:建議使用固定長度的資料列代替可變長度的資料列。
- MEMORY 儲存引擎:目前都使用固定長度的資料行儲存,因此無論使用 CHAR 或VARCHAR 列都沒有關係。兩者都是作為 CHAR 型別處理。
- InnoDB 儲存引擎:建議使用 VARCHAR 型別。對於 InnoDB 資料表,內部的行儲存格式沒有區分固定長度和可變長度列(所有資料行都使用指向資料列值的頭指標),因此在本質上,使用固定長度的 CHAR 列不一定比使用可變長度 VARCHAR 列效能要好。因而,主要的效能因素是資料行使用的儲存總量。由於 CHAR 平均佔用的空間多於 VARCHAR,因此使用 VARCHAR 來最小化需要處理的資料行的儲存總量和磁碟 I/O 是比較好的。
8.2 TEXT與BLOB
char或者varchar適用於儲存少量字串,如果儲存較大文字是,通常會選擇使用TEXT和BLOB。
BLOB:用來儲存二進位制資料,比如照片。
TEXT:只能儲存字元資料,比如一篇文章和日記。
TEXT 和 BLOB 中又分別包括TEXT、MEDIUMTEXT、LONGTEXT 和 BLOB、MEDIUMBLOB、LONGBLOB3 種不同的型別。
(1)BLOB 和 TEXT 值會引起一些效能問題,特別是在執行了大量的刪除操作時。 刪除操作會在資料表中留下很大的“空洞”,以後填入這些“空洞”的記錄在插入的效能上會有影響。為了提高效能,建議定期使用 OPTIMIZE TABLE
功能對這類表進行碎片整理,避免因為“空洞”導致效能問題。
(2)可以使用合成的(Synthetic)索引來提高大文字欄位(BLOB 或 TEXT)的查詢效能
簡單來說,合成索引就是根據大文字欄位的內容建立一個雜湊值,並把這個值儲存在單獨的資料列中,接下來就可以通過檢索雜湊值找到資料行了。但是,要注意這種技術只能用於精確匹配的查詢(雜湊值對於類似<或>=等範圍搜尋操作符是沒有用處的)。
mysql> create table t (id varchar(100),context blob,hash_value varchar(40));
Query OK, 0 rows affected (0.02 sec)
mysql> insert into t values(1,repeat('beijing',2),md5(context));
Query OK, 1 row affected (0.01 sec)
mysql> insert into t values(2,repeat('beijing',2),md5(context));
Query OK, 1 row affected (0.01 sec)
mysql> insert into t values(3,repeat('beijing 2008',2),md5(context));
Query OK, 1 row affected (0.00 sec)
mysql> select * from t;
+------+--------------------------+----------------------------------+
| id | context | hash_value |
+------+--------------------------+----------------------------------+
| 1 | beijingbeijing | 09746eef633dbbccb7997dfd795cff17 |
| 2 | beijingbeijing | 09746eef633dbbccb7997dfd795cff17 |
| 3 | beijing 2008beijing 2008 | 1c0ddb82cca9ed63e1cacbddd3f74082 |
+------+--------------------------+----------------------------------+
3 rows in set (0.00 sec)
mysql> select * from t where hash_value=md5(repeat('beijing 2008',2));
+------+--------------------------+----------------------------------+
| id | context | hash_value |
+------+--------------------------+----------------------------------+
| 3 | beijing 2008beijing 2008 | 1c0ddb82cca9ed63e1cacbddd3f74082 |
+------+--------------------------+----------------------------------+
(3)對 BLOB 或者 CLOB 欄位進行模糊查詢,MySQL 提供了字首索引,也就是隻為欄位的前 n 列建立索引。
mysql> create index idx_blob on t(context(100));
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc select * from t where context like 'beijing%' \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t
partitions: NULL
type: ALL
possible_keys: idx_blob
key: NULL
key_len: NULL
ref: NULL
rows: 3
filtered: 100.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)
# 請注意,這裡的查詢條件中,“%”不能放在最前面,否則索引將不會被使用。
(4)在不必要的時候避免檢索大型的 BLOB 或 TEXT 值。
(5)把 BLOB 或 TEXT 列分離到單獨的表中。
8.3 浮點數與定點數
浮點數一般用於表示含有小數部分的數值。插入資料超出實際精度,則插入值會被四捨五入到實際定義的精度值,然後插入,四捨五入的過程不會報錯。在 MySQL 中 float、double(或 real)用來表示浮點數。
定點數實際上是以字串形式存放的,定點數可以更加精確的儲存資料。如果實際插入的數值精度大於實際定義的精度,則 MySQL 會進行警告(預設的 SQLMode 下),但是資料按照實際精度四捨五入後插入;如果 SQLMode 是在 TRADITIONAL傳統模式下,則系統會直接報錯,導致資料無法插入。在 MySQL 中,decimal(或 hnumberic)用來表示定點數。
注意:在今後關於浮點數和定點數的應用中,使用者要考慮到以下幾個原則:
- 浮點數存在誤差問題;
- 對貨幣等對精度敏感的資料,應該用定點數表示或儲存;
- 在程式設計中,如果用到浮點數,要特別注意誤差問題,並儘量避免做浮點數比較;
- 要注意浮點數中一些特殊值的處理。
8.4 日期型別選擇
選擇日期型別的原則:
- 如果只需要記錄年份,只需要用一個位元組的
YEAR
型別完全可以滿足,而不需要四個位元組的DATE
型別。 - 如果要記錄年月日時分秒,並且記錄年份比較久遠,那麼最好使用
DATETIME
。 - 如果要記錄的日期要讓不同時區的使用者使用,那麼最好使用
TIMESTAMP
。
延展閱讀
mysql的嚴格模式和寬鬆模式
mysql支援的sql_mode模式:ANSI、TRADITIONAL、STRICT_ALL_TABLES和STRICT_TRANS_TABLES。
ANSI模式:寬鬆模式,對插入資料進行校驗,如果不符合定義型別或長度,對資料型別調整或截斷儲存,報warning警告。
TRADITIONAL模式:嚴格模式,當向mysql資料庫插入資料時,進行資料的嚴格校驗,保證錯誤資料不能插入,報error錯誤。用於事務時,會進行事務的回滾。
STRICT_TRANS_TABLES模式:嚴格模式,進行資料的嚴格校驗,錯誤資料不能插入,報error錯誤。只對支援事務的表有效。
STRICT_ALL_TABLES模式:嚴格模式,進行資料的嚴格校驗,錯誤資料不能插入,報error錯誤。對所有表都有效。
# 檢視當前模式
select @@sql_mode;
show variables like '%mode%';
# 更改模式值:(1)命令列 (2)配置檔案 更多詳情見官網
# 有時候當像資料庫插入一個date型別為空或者不允許插入的字元的時候,可以通過設定sql_mode來允許操作
# 清空模式值[區域性]
set [global | session] sql_mode = "";