一直以为int(M) 中M代表只能存储M个字符的数据,比如int(2),只能存储0-99 ,偶然发现这是错误的想法,测试如下:
create table testInt(id1 int(1),id2 tinyint(1));
INSERT INTO testInt(id1,id2) values(127,127); -- 运行成功
从结果来看,问题中的想法确实错误
INSERT INTO testInt(id1,id2) values(128,128);
-- error 1264 - Out of range value for column 'id2' at row 1
出错了,int类型的id1插入成功了,tinyint类型的id2提示超出了范围,这是为什么呢?下面看一张表
上述表格中的数值类型都是定长的,也就是说,无论你存的数值是多少,多大或者多小,占用的字节大小都是固定的。
到这里,我们已经可以发现,M值即使设置为1,它也可以存入字符长度大于1的值,那么,如果存入的字符长度小于1会怎么样?我们来试一试:
先将id1的类型更改为int(2),然后插入数据id1=1
ALTER TABLE testInt Modify id1 int(2);
INSERT INTO testInt(id1) values(1); -- 运行成功
我们查询一下表中的数据,看看结果具体如何:
mysql> select * from testInt;
+-----+------+| id1 | id2 |
+-----+------+| 1 | NULL |
接下来,我们再修改一下id1的填充数据类型zerofill(表示用0填充),这里先知道如何操作即可,我们再从结果得出结论:
提示:命令行查询才会显示01,navicat工具查询只会显示1
mysql> ALTER TABLE testInt MODIFY id1 int(2) zerofill;
mysql> select * from testInt;
+-----+------+| id1 | id2 |
+-----+------+| 01 | NULL |
现在是不是有些清楚了。我们设置的M值是2,没有设置zerofill用0填充时,对于操作没有任何影响,而设置了zerofill后,我们可以清楚地看到值1字符数不足M值,左前位置补0
从上面我们可以得到如下的结论:
1、整数型的数值类型已经限制了取值范围,有符号整型和无符号整型都有,而M值并不代表可以存储的数值字符长度,它代表的是数据在显示时显示的最小长度;
2、当存储的字符长度超过M值时,没有任何的影响,只要不超过数值类型限制的范围;
3、当存储的字符长度小于M值时,只有在设置了zerofill用0来填充,才能够看到效果,换句话就是说,没有zerofill,M值就是无用的。
char定长字段
我们定义了 char(20),那么我们我们存什么值,在存储层都是占用20个长度的,一般数据库都是 在右侧进行补足长度,当然这造成了硬盘存储存储的浪费
varchar 变长字段
我们定义了varchar(20),我们存啥值,就是啥值,不会进行长度补足,在存储方面比较节省空间
内存差异
char(20) 和 varchar(20),虽然硬盘存储上varchar有一定的优点,但是从page读取到内存之后,两者在内存中占用的大小是一样多的,并不以varchar事件存储的大小为准,这是为什么呢?其实我们想想,我们需要对varchar字段进行更新,从长度2更新为长度10,那么如果按该字段实际大小分配内存在更新后就会导致内存混乱,所以所有字段进内存时不管该字段类型是变长还是定长数据库都会以定长最大长度方式进入内存
使用建议
针对需要频繁改动的字段建议使用char类型,针对基本不会变化的字符使用varchar类型以节省存储空间,如果需要创建索引那么也建议使用char类型,因为char类型能有效避免因字段变化而产生的索引碎片,提高索引性能
varchar的实际长度
varchar(20) 这个20 是代表能存储多少个字符长度,注意是长度,所以这长度是根据各个字符集不同而会不同,我们来看下常见的字符集 utf8 和 utf8mb4 各能存储多长字段
utf8
create table a (a varchar(65535)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Code: 1074. Column length too big for column 'a' (max = 21845); use BLOB or TEXT instead 0.000 sec
utf8mb4
create table a (a varchar(65535)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 ;
Error Code: 1074. Column length too big for column 'a' (max = 16383); use BLOB or TEXT instead 0.000 sec
utf8最大字符是3个字节,而utf8mb4 是4个字节,所以 使用65535 分别除 3 和4 结果就是 21845 和 16383
字符类型长度修改建议
字符的长度随着业务需求有所变化,需要变长或变短,因此在设计表时候要充分考虑字段长度的后续包容性,确保后期不会因为长度而满足不了业务需求,而贸然去修改字符长度(变短情况可忽略),mysql中并没有像sqlserver 中的修改表元数据的优化
(sql server有些字符长度变化是不会导致表重构,只是修改表的元数据定义而已,如 varchar(10)修改为varchar(20) 并不会导致表重构),大多数alter table语句都会导致mysql表进行重构操作,哪怕仅仅是修改下字符长度,当然后续版本可能有所改进,
目前而言,除非我们非常确定某个alter语句不会导致表重构,否则不要贸然在生产环境进行数据结构变化。所以针对小表可在服务器不忙时间直接修改,而针对大表则建议