腦補,varchar(N),N指的是最大字符數,不是字節數。
先上測試說明:
在MySQL建表時,遇到一個奇怪的現象:
root@localhost : test 10:30:54>CREATE TABLE tb_test ( -> recordid varchar(32) NOT NULL, -> areaShow varchar(10000) DEFAULT NULL, -> areaShow1 varchar(10000) DEFAULT NULL, -> areaShow2 varchar(10000) DEFAULT NULL, -> PRIMARY KEY (recordid) -> ) ENGINE=INNODB 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 報錯
root@localhost : test 10:31:01>CREATE TABLE tb_test ( -> recordid varchar(32) NOT NULL, -> areaShow varchar(30000) DEFAULT NULL, -> areaShow1 varchar(30000) DEFAULT NULL, -> areaShow2 varchar(30000) DEFAULT NULL, -> PRIMARY KEY (recordid) -> ) ENGINE=INNODB DEFAULT CHARSET=utf8; Query OK, 0 rows affected, 3 warnings (0.26 sec) 可以建立,只是類型被轉換了。
root@localhost : test 10:31:14>show warnings; +-------+------+----------------------------------------------------+ | Level | Code | Message | +-------+------+----------------------------------------------------+ | Note | 1246 | Converting column 'areaShow' from VARCHAR to TEXT | | Note | 1246 | Converting column 'areaShow1' from VARCHAR to TEXT | | Note | 1246 | Converting column 'areaShow2' from VARCHAR to TEXT | +-------+------+----------------------------------------------------+ 3 rows in set (0.00 sec)
疑問:
為什么字段小(10000)的反而報錯,而大(30000)的則可以建立。為什么小的不能直接轉換呢?
解決:
這里多感謝orczhou的幫助,原來MySQL在建表的時候有個限制:MySQL要求一個行的定義長度不能超過65535。具體的原因可以看:
http://dev.mysql.com/doc/refman/5.1/en/silent-column-changes.html
(1)單個字段如果大於65535,則轉換為TEXT 。
(2)單行最大限制為65535,這里不包括TEXT、BLOB。
按照上面總結的限制,來解釋出現的現象:
第一個情況是:
單個字段長度:varchar(10000) ,字節數:10000*3(utf8)+(1 or 2) = 30000 ,小於65535,可以建立。
單行記錄長度:varchar(10000)*3,字節數:30000*3(utf8)+(1 or 2) = 90000,大於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
第二個情況是:
單個字段長度:varchar(30000) ,字節數:30000*3+(1 or 2) = 90000 , 大於65535,需要轉換成TEXT,才可以建立。所以報warnings。
單行記錄長度:varchar(30000)*3,因為每個字段都被轉換成了TEXT,而TEXT沒有限制,所以可以建立表。
root@localhost : test 10:31:14>show warnings; +-------+------+----------------------------------------------------+ | Level | Code | Message | +-------+------+----------------------------------------------------+ | Note | 1246 | Converting column 'areaShow' from VARCHAR to TEXT | | Note | 1246 | Converting column 'areaShow1' from VARCHAR to TEXT | | Note | 1246 | Converting column 'areaShow2' from VARCHAR to TEXT | +-------+------+----------------------------------------------------+
用了這么久的MySQL,這個基本的建表限制都還不知道,慚愧啊。。
原因如下:
被問到一個問題:MySQL中varchar最大長度是多少?這不是一個固定的數字。本文簡要說明一下限制規則。
1、限制規則
字段的限制在字段定義的時候有以下規則:
a) 存儲限制
varchar 字段是將實際內容單獨存儲在聚簇索引之外,內容開頭用1到2個字節表示實際長度(長度超過255時需要2個字節),因此最大長度不能超過65535。
b) 編碼長度限制
字符類型若為gbk,每個字符最多占2個字節,最大長度不能超過32766;
字符類型若為utf8,每個字符最多占3個字節,最大長度不能超過21845。
若定義的時候超過上述限制,則varchar字段會被強行轉為text類型,並產生warning。
c) 行長度限制
導致實際應用中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。
2、計算例子
舉兩個例說明一下實際長度的計算。
a) 若一個表只有一個varchar類型,如定義為
create table t4(c varchar(N)) charset=gbk;
則此處N的最大值為(65535-1-2)/2= 32766。
減1的原因是實際行存儲從第二個字節開始’;
減2的原因是varchar頭部的2個字節表示長度;
除2的原因是字符編碼是gbk。
b) 若一個表定義為
create table t4(c int, c2 char(30), c3 varchar(N)) charset=utf8;
則此處N的最大值為 (65535-1-2-4-30*3)/3=21812
減1和減2與上例相同;
減4的原因是int類型的c占4個字節;
減30*3的原因是char(30)占用90個字節,編碼是utf8。
如果被varchar超過上述的b規則,被強轉成text類型,則每個字段占用定義長度為11字節,當然這已經不是“varchar”了。