MySQL存儲秘密揭示:CHAR vs.VARCHAR,解鎖定長神器的終極選擇指南!
CHAR和VARCHAR類型在存儲和檢索方式上有相似之處,但在最大長度以及是否保留尾隨空格方面存在差異。
一、存儲方面
1、存儲對比
CHAR和VARCHAR類型的聲明包含一個長度,表示要存儲的最大字符數(shù)。例如,CHAR(30)可以容納最多30個字符。
CHAR列的長度在創(chuàng)建表時被固定為您聲明的長度。長度可以是從0到255的任何值。當(dāng)存儲CHAR值時,它們會被右填充到指定的長度。當(dāng)檢索CHAR值時,除非啟用了PAD_CHAR_TO_FULL_LENGTH SQL模式(默認(rèn)沒啟用),否則會刪除尾隨空格。
VARCHAR列中的值是可變長度字符串。長度可以指定為0到65,535的值。VARCHAR的有效最大長度取決于最大行大?。?5,535字節(jié),這個大小被所有列共享)和所使用的字符集。
2、具體對比
假設(shè)當(dāng)前一個表里包含類型為char(4)及varchar(4)的列(假設(shè)該列使用單字節(jié)字符集如latin1),則,存儲所需的大小對比如下:
注:
- 如果字符集為其他類型(utf8mb4),則根據(jù)存儲的內(nèi)容進(jìn)行換算即可
- 上例中為了演示,非嚴(yán)格模式時可以截斷存儲
二、顯示及檢索對比
本文后續(xù)案例是在utf8的字符集的實例上進(jìn)行演示的
1、默認(rèn)SQL模式
默認(rèn)沒開啟PAD_CHAR_TO_FULL_LENGTH SQL模式的情況下,char及varchar的顯示幾乎沒有區(qū)別,例如:
mysql> create table tb_char (id int primary key auto_increment, c_char char(10),c_varchar varchar(10));
Query OK, 0 rows affected (0.01 sec)
mysql> insert into tb_char (c_char,c_varchar) values ('a','a'),('張三','張三'),('tc一','tc一');
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
查詢顯示如下:
mysql> select * from tb_char;
+----+--------+-----------+
| id | c_char | c_varchar |
+----+--------+-----------+
| 1 | a | a |
| 2 | 張三 | 張三 |
| 3 | tc一 | tc一 |
+----+--------+-----------+
3 rows in set (0.00 sec)
查看長度如下:
mysql> select c_char,c_varchar, length(c_char)len_char,length(c_varchar)len_varchar,char_length(c_char),char_length(c_varchar) from tb_char;
+--------+-----------+----------+-------------+---------------------+------------------------+
| c_char | c_varchar | len_char | len_varchar | char_length(c_char) | char_length(c_varchar) |
+--------+-----------+----------+-------------+---------------------+------------------------+
| a | a | 1 | 1 | 1 | 1 |
| 張三 | 張三 | 6 | 6 | 2 | 2 |
| tc一 | tc一 | 5 | 5 | 3 | 3 |
+--------+-----------+----------+-------------+---------------------+------------------------+
3 rows in set (0.00 sec)
從顯示結(jié)果來看,char與varchar在此時看上去是一致的,即:查詢時,char類型的數(shù)據(jù)會將末尾空格去除。
此時,如果寫入末尾帶空格的值,則結(jié)果會如何呢?
mysql> insert into tb_char (c_char,c_varchar) values ('abc ','abc ');
Query OK, 1 row affected (0.01 sec)
mysql> select * from tb_char;
+----+--------+-----------+
| id | c_char | c_varchar |
+----+--------+-----------+
| 1 | a | a |
| 2 | 張三 | 張三 |
| 3 | tc一 | tc一 |
| 4 | abc | abc |
+----+--------+-----------+
4 rows in set (0.00 sec)
mysql> select c_char,c_varchar, length(c_char)len_char,length(c_varchar)len_varchar,char_length(c_char),char_length(c_varchar) from tb_char;
+--------+-----------+----------+-------------+---------------------+------------------------+
| c_char | c_varchar | len_char | len_varchar | char_length(c_char) | char_length(c_varchar) |
+--------+-----------+----------+-------------+---------------------+------------------------+
| a | a | 1 | 1 | 1 | 1 |
| 張三 | 張三 | 6 | 6 | 2 | 2 |
| tc一 | tc一 | 5 | 5 | 3 | 3 |
| abc | abc | 3 | 4 | 3 | 4 |
+--------+-----------+----------+-------------+---------------------+------------------------+
4 rows in set (0.00 sec)
此時,結(jié)果出現(xiàn)了不同,char類型的顯示依舊將末尾空格去除,而varchar則原樣顯示
如果此時按條件查詢‘a(chǎn)bc’及‘a(chǎn)bc ’,結(jié)果會如何?
mysql> select * from tb_char where c_char ='abc';
+----+--------+-----------+
| id | c_char | c_varchar |
+----+--------+-----------+
| 4 | abc | abc |
+----+--------+-----------+
1 row in set (0.00 sec)
mysql> select * from tb_char where c_varchar ='abc';
+----+--------+-----------+
| id | c_char | c_varchar |
+----+--------+-----------+
| 4 | abc | abc |
+----+--------+-----------+
1 row in set (0.00 sec)
mysql> select * from tb_char where c_char ='abc ';
+----+--------+-----------+
| id | c_char | c_varchar |
+----+--------+-----------+
| 4 | abc | abc |
+----+--------+-----------+
1 row in set (0.00 sec)
mysql> select * from tb_char where c_varchar ='abc ';
+----+--------+-----------+
| id | c_char | c_varchar |
+----+--------+-----------+
| 4 | abc | abc |
+----+--------+-----------+
1 row in set (0.00 sec)
結(jié)果是都可以顯示。
不過,如果做了concat操作,結(jié)果如下:
mysql> select c_char,c_varchar,concat(c_char,'concat'),concat(c_varchar,'concat') from tb_char;
+--------+-----------+-------------------------+----------------------------+
| c_char | c_varchar | concat(c_char,'concat') | concat(c_varchar,'concat') |
+--------+-----------+-------------------------+----------------------------+
| a | a | aconcat | aconcat |
| 張三 | 張三 | 張三concat | 張三concat |
| tc一 | tc一 | tc一concat | tc一concat |
| abc | abc | abcconcat | abc concat |
+--------+-----------+-------------------------+----------------------------+
2、PAD_CHAR_TO_FULL_LENGTH 模式
將SQL模式改為PAD_CHAR_TO_FULL_LENGTH后,再對比看一下。
mysql> set sql_mode = 'PAD_CHAR_TO_FULL_LENGTH';
Query OK, 0 rows affected (0.00 sec)
mysql> select * from tb_char;
+----+----------------+-----------+
| id | c_char | c_varchar |
+----+----------------+-----------+
| 1 | a | a |
| 2 | 張三 | 張三 |
| 3 | tc一 | tc一 |
| 4 | abc | abc |
+----+----------------+-----------+
4 rows in set (0.00 sec)
mysql> select c_char,c_varchar, length(c_char)len_char,length(c_varchar)len_varchar,char_length(c_char),char_length(c_varchar) from tb_char;
+----------------+-----------+----------+-------------+---------------------+------------------------+
| c_char | c_varchar | len_char | len_varchar | char_length(c_char) | char_length(c_varchar) |
+----------------+-----------+----------+-------------+---------------------+------------------------+
| a | a | 10 | 1 | 10 | 1 |
| 張三 | 張三 | 14 | 6 | 10 | 2 |
| tc一 | tc一 | 12 | 5 | 10 | 3 |
| abc | abc | 10 | 4 | 10 | 4 |
+----------------+-----------+----------+-------------+---------------------+------------------------+
4 rows in set (0.00 sec)
此時使用concat,結(jié)果如下:
mysql> select c_char,c_varchar,concat(c_char,'concat'),concat(c_varchar,'concat') from tb_char;
+----------------+-----------+-------------------------+----------------------------+
| c_char | c_varchar | concat(c_char,'concat') | concat(c_varchar,'concat') |
+----------------+-----------+-------------------------+----------------------------+
| a | a | a concat | aconcat |
| 張三 | 張三 | 張三 concat | 張三concat |
| tc一 | tc一 | tc一 concat | tc一concat |
| abc | abc | abc concat | abc concat |
+----------------+-----------+-------------------------+----------------------------+
4 rows in set (0.00 sec)
可見,此時char類型每一行的末尾都已經(jīng)填充了空格。
使用‘a(chǎn)bc’及‘a(chǎn)bc ’查看結(jié)果:
mysql> select * from tb_char where c_char ='abc';
+----+------------+-----------+
| id | c_char | c_varchar |
+----+------------+-----------+
| 4 | abc | abc |
+----+------------+-----------+
1 row in set (0.00 sec)
mysql> select * from tb_char where c_varchar ='abc';
+----+------------+-----------+
| id | c_char | c_varchar |
+----+------------+-----------+
| 4 | abc | abc |
+----+------------+-----------+
1 row in set (0.00 sec)
mysql> select * from tb_char where c_char ='abc ';
+----+------------+-----------+
| id | c_char | c_varchar |
+----+------------+-----------+
| 4 | abc | abc |
+----+------------+-----------+
1 row in set (0.00 sec)
mysql> select * from tb_char where c_varchar ='abc ';
+----+------------+-----------+
| id | c_char | c_varchar |
+----+------------+-----------+
| 4 | abc | abc |
+----+------------+-----------+
1 row in set (0.00 sec)
mysql> select * from tb_char where c_char ='abc ';
+----+------------+-----------+
| id | c_char | c_varchar |
+----+------------+-----------+
| 4 | abc | abc |
+----+------------+-----------+
1 row in set (0.00 sec)
該結(jié)果是沒有區(qū)別的。
三、字符串長度查看函數(shù)
在MySQL中,字符串計算函數(shù)有如下幾種,在使用是注意區(qū)分。
1、LENGTH 函數(shù)
用于返回字符串的字節(jié)數(shù)。對于英文字符,一個字符占用一個字節(jié);而對于一些多字節(jié)字符(如UTF-8編碼的中文字符),一個字符可能占用多個字節(jié)。
SELECT LENGTH('Hello'); -- 返回 5
SELECT LENGTH('你好'); -- 返回 6(UTF-8編碼中文字符占用3個字節(jié))
2、CHAR_LENGTH 函數(shù)
用于返回字符串的字符數(shù),而不是字節(jié)數(shù)。這意味著它會考慮字符的多字節(jié)性,確保正確地計算字符的數(shù)量。
SELECT CHAR_LENGTH('Hello'); -- 返回 5
SELECT CHAR_LENGTH('你好'); -- 返回 2(兩個中文字符)
3、CHARACTER_LENGTH 函數(shù)
與 CHAR_LENGTH 函數(shù)功能相似,也用于返回字符串的字符數(shù)。
SELECT CHARACTER_LENGTH('Hello'); -- 返回 5
SELECT CHARACTER_LENGTH('你好'); -- 返回 2
4、BIT_LENGTH 函數(shù)
返回字符串的位數(shù),而不是字節(jié)數(shù)或字符數(shù)。
SELECT BIT_LENGTH('Hello'); -- 返回 40(5個英文字符,每個字符8位)
SELECT BIT_LENGTH('你好'); -- 返回 48(2個中文字符,每個字符24位)
四、結(jié)語
了解CHAR和VARCHAR類型的差異對于有效地設(shè)計和管理MySQL數(shù)據(jù)庫極為重要。選擇合適的數(shù)據(jù)類型和了解它們的行為有助于避免意外的數(shù)據(jù)處理問題。同時,開發(fā)人員應(yīng)根據(jù)具體需求和預(yù)期行為來選擇合適的數(shù)據(jù)類型,以確保數(shù)據(jù)存儲和檢索的一致性和準(zhǔn)確性。