Mysql中varchar型別的貓膩!
阿新 • • 發佈:2021-01-09
### varchar的儲存規則
4.0版本以下,varchar(20),指的是20位元組,如果存放UTF8漢字時,只能存6個(每個漢字3位元組)。
5.0版本以上,varchar(20),指的是20字元,無論存放的是數字、字母還是UTF8漢字(每個漢字3位元組),都可以存放20個,最大大小是65532位元組。
varchar 欄位是將實際內容單獨儲存在聚簇索引之外,內容開頭用1到2個位元組表示實際長度。
官方是這麼說的:
Values in VARCHAR columns are variable-length strings. The length can be specified as a value from 0 to 255 before MySQL 5.0.3, and 0 to 65,535 in 5.0.3 and later versions.
In contrast to CHAR, VARCHAR values are stored as a one-byte or two-byte length prefix plus data. The length prefix indicates the number of bytes in the value.
A column uses one length byte if values require no more than 255 bytes, two length bytes if values may require more than 255 bytes.
### varchar和char 的區別
#### 區別一,定長和變長
char 表示定長,長度固定,varchar表示變長,即長度可變。當所插入的字串超出它們的長度時,視情況來處理,如果是嚴格模式,則會拒絕插入並提示錯誤資訊,如果是寬鬆模式,則會擷取然後插入。如果插入的字串長度小於定義長度時,則會以不同的方式來處理,如char(10),表示儲存的是10個字元,無論你插入的是多少,都是10個,如果少於10個,則用空格填滿。而varchar(10),小於10個的話,則插入多少個字元就存多少個。
varchar怎麼知道所儲存字串的長度呢?實際上,對於varchar欄位來說,需要使用一個(如果字串長度小於255)或兩個位元組(長度大於255)來儲存字串的長度。但是因為他需要有一個prefix來表示他具體bytes數是多少(因為varchar是變長的,沒有這個長度值他不知道如何讀取資料)。
#### 區別之二,儲存的容量不同
對 char 來說,最多能存放的字元個數 255,和編碼無關。
而 varchar 呢,最多能存放 65532 個字元。VARCHAR 的最大有效長度由最大行大小和使用的字符集確定。整體最大長度是 65,532位元組
### varchar的編碼長度限制
字元型別若為 gbk,則個字元最多佔2個位元組,最大長度不能超過32766; 字元型別若為utf8,則每個字元最多佔3個位元組,最大長度不能超過21845。 若定義的時候超過上述限制,則varchar欄位會被強行轉為text型別,併產生warning。
### 行長度限制
導致實際應用中varchar長度限制的是一個行定義的長度。 MySQL要求一個行的定義長度不能超過65535。若定義的表長度超過這個值,則提示 ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. You have to change some columns to TEXT or BLOBs。
這就是說,比如建立一個表,表結構中有兩個varhcar型別欄位,那麼這兩個欄位的總長度不能超過65535。
官方說明如下:
Every table has a maximum row size of 65,535 bytes.
This maximum applies to all storage engines, but a given engine might have additional constraints that result in a lower effective maximum row size.
### varchar的控制位
MySQL 中的Varchar字元型別還保留了1個位元組來留其它控制資訊。
### 示例
#### 示例一:若一張表中只有一個欄位VARCHAR(N)型別,utf8編碼,則N最大值為多少?
如:create table tb_name1(a varchar(N)) default charset=utf8,則N最大值=(65535-1-2)/3=21844。
減1的原因是實際行儲存從第二個位元組開始。
減2的原因是varchar頭部的2個位元組表示長度。
除3的原因是字元編碼是utf8。
sql測試:
```
create table tb_name1(a varchar(21844)) default charset=utf8;
Query OK, 0 rows affected (0.38 sec)
drop table tb_name1;
Query OK, 0 rows affected (0.00 sec)
create table tb_name1(a varchar(21845)) default charset=utf8;
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. You have to change some columns
```
#### 示例二:若一張表中有一個欄位VARCHAR(N)型別,並且有其它的欄位型別,utf8編碼,則N的最大值為多少?
如:create table tb_name2(a int, b char(20), c varchar(N)) default charset=utf8;
則:N最大值=(65535-1-2-4-20*3)/3=21822
減1的原因是實際行儲存從第二個位元組開始。
減2的原因是varchar頭部的2個位元組表示長度。
減4的原因是a欄位的int型別佔4個位元組。
減20*3的原因是char(20)佔用60個位元組,編碼是utf8。
sql測試:
```
create table tb_name2(a int, b char(20), c varchar(21822)) default charset=utf8;
Query OK, 0 rows affected (0.28 sec)
drop table tb_name2;
Query OK, 0 rows affected (0.20 sec)
create table tb_name2(a int, b char(20), c varchar(21823)) default charset=utf8;
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. You have to change some columns to TEXT or BLOBs
```
#### 例項三:若一張表中有多欄位VARCHAR(N)型別,並且有其它的欄位型別,gbk編碼,則N的最大值為多少?
如:create table tb_name3(a int, b char(20), c varchar(50), d varchar(N)) default charset=gbk;
則:N最大值=(65535-1-1-2-4-20*2-50*2)/2=32693
第一個減1的原因是實際行儲存從第二個位元組開始。
第二個減1表示第二個varchar(50)頭部一個1個位元組表示長度(小於255)。
減2的原因是varchar頭部的2個位元組表示長度。
減20*2的原因是char(20)佔用40個位元組,編碼是gbk。
減50*2的原因是varchar(50)佔用100個位元組,編碼是gbk。
SQL測試:
```
create table tb_name3(a int, b char(20), c varchar(50), d varchar(32694)) default charset=gbk;
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. You have to change some columns to TEXT or BLOBs
```
```
create table tb_name3(a int, b char(20), c varchar(50), d varchar(32693)) default charset=gbk;
Query OK, 0 rows affected (0.18 sec)
```