备注:测试数据库版本为MySQL 8.0
这个blog我们来聊聊MySQL支持的数据类型
类型 | 存储(字节) | 最小(有符号) | 最大(有符号) | 最小(无符号) | 最大(无符号) | 描述 |
---|---|---|---|---|---|---|
BIT(M) | (m+7)/8 | — | — | — | — | 位值类型。M表示每个值的位数,从1到64.如果M省略,默认是1。比如bit(8)存储888变为00000111 |
TINYINT(M) | 1 | -128 | 127 | 0 | 255 | |
SMALLINT(M) | 2 | -32768 | 32767 | 0 | 65535 | |
MEDIUMINT(M) | 3 | -8388608 | 8388607 | 0 | 16777215 | |
INT,INTEGER(M) | 4 | -2147483648 | 2147483647 | 0 | 4294967295 | |
BIGINT(M) | 8 | -2^63 | 2^63 -1 | 0 | 2^64 | |
DECIMAL | 变化 | M为总位数(精度),D为小数点后的位数(刻度)。如果D为0,则值没有小数部分。最大(M)是65。最大(D)为30.如果省略D,D的默认值为0,。如果省略M,M的默认值为10. NUMBERIC的实现是DECIMAL | ||||
NUMBERIC | 变化 | 同上 | ||||
FLOAT(M,D) | 4 | M是总位数,D是小数点后面的位数。如果M和D省略,则将值存储到硬件允许的限制。单精度浮点精确到7位小数。 正区间- [ –3.402823466E38 , –1.175494351E-38 ] 负区间-[ 1.175494351E-38 , 3.402823466E38] |
||||
DOUBLE(M,D) | 8 | M是总位数,D是小数点后面的位数。如果M和D省略,则将值存储到硬件允许的限制。单精度浮点精确到15位小数。 正区间-[ –1.7976931348623157E308,–2.2250738585072014E-308 ] 负区间-[ 2.2250738585072014E-308 , 1.7976931348623157E308 ] |
||||
BOOL,BOOLEAN | 1 | TINYINT(1)的同义词 |
总结:
1.不允许负数
整数类型有可选的UNSIGNED属性,表示不允许存负值,这大概可以使整数的上限提高一倍。
如: TINYINT UNSIGNED
2.如何选择合适的数值类型
1.整数类型
例如枚举类的, 选择 tinyint、smallint即可,节省磁盘空间就是优化。
其它的业务相关表,例如用户表、订单表 可以选择用 int类型。
虽然int类型不支持小数,但是例如金额这个,可以通过调整单位,例如单位为分,这样就可以存小数金额了
对于一些大的日志表、分布式ID之类的,可以选择bigint类型
2.小数类型
对于需要存储小数的场景而言,使用decimal(m,d)
m代表总的位数,d代表小数位,整数位为m-d
create table t_decimal(id decimal(7,4));
-- 小数位不够会自动填0
insert into t_decimal values (123.456);
-- 这个是正常的插入方法
insert into t_decimal values (123.4561);
-- 整数为超过限制 报错
insert into t_decimal values (1230.4561);
-- 小数位超过限制 报错
insert into t_decimal values (1230.45612);
执行记录:
mysql> create table t_decimal(id decimal(7,4));
Query OK, 0 rows affected (0.03 sec)
mysql> -- 小数位不够会自动填0
mysql> insert into t_decimal values (123.456);
Query OK, 1 row affected (0.00 sec)
mysql> -- 这个是正常的插入方法
mysql> insert into t_decimal values (123.4561);
Query OK, 1 row affected (0.00 sec)
mysql> -- 整数为超过限制 报错
mysql> insert into t_decimal values (1230.4561);
ERROR 1264 (22003): Out of range value for column 'id' at row 1
mysql> -- 小数位超过限制 报错
mysql> insert into t_decimal values (1230.45612);
ERROR 1264 (22003): Out of range value for column 'id' at row 1
mysql>
mysql> select * from t_decimal;
+----------+
| id |
+----------+
| 123.4560 |
| 123.4561 |
+----------+
2 rows in set (0.00 sec)
3.浮点数
浮点数会存在一定的精度的缺失,但是可以应用于科学计算,性能会比整数和小数类都要快。
类型 | 存储(字节) | 范围 | 格式 | 用途 |
---|---|---|---|---|
DATE | 3 | 1000-01-01/9999-12-31 | YYYY-MM-DD | 日期值 |
TIME | 3 | ‘-838:59:59’/‘838:59:59’ | HH:MM:SS | 时间值或持续时间 |
YEAR | 1 | 1901/2155 | YYYY | 年份值 |
DATETIME | 8 | 1000-01-01 00:00:00/9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值 |
TIMESTAMP | 4 | 1970-01-01 00:00:00/2038 | YYYYMMDD HHMMSS | 混合日期和时间值,时间戳 |
关于时间类型的选择:
1.如果只存年,用YEAR类型
2.如果只存年月日,用DATE
3.如果需要存年月日时分秒,用TIMESTAMP
不要被这个2038年给吓到了,而不用TIMESTAMP,其实更节约存储空间,且能容纳时区信息
4.不用将TIMESTAMP转换为数值
FROM_UNIXTIME() – 把数值转换为时间戳
UNIX_TIMESTAMP() – 把时间戳转换为数值
转换感觉是节省了空间,不过处理起来非常的不方便,不推荐使用
CHAR和VARCHAR类型
类型 | 存储(字节) | 范围 | 用途 |
---|---|---|---|
CHAR(M) | M | 0 - 255 | 存储定长的字符 |
VARCHAR(M) | VARCHAR(10) 实际存储3个字符,1个字节来存储长度,总共占4字节 VARCHAR(1000) 实际存储3个字符,2个字节来存储长度,总共占5字节 不同的存储引擎可能存在一定的差异 |
0-65536 | 存储可变长度的字符串 |
1.类型选择问题
很多时候,开发同事为了方便,直接用varchar(200) 来存储字符,不考虑实际需求。
这样做,存在诸多弊端。
如果是md5密码这样的定长字段,如果用varchar类型,会浪费一定的存储空间。
如果存储的字符只有5个,而这时都用varchar(200),感觉存储空间是一样的。但是程序端读取的时候,varchar(200)会消耗更多的内存。
2.变长字符的更新问题
InnoDB存储引擎
varchar由于是变长,遇到更新的时候,如果比原先的长度长很多,这个时候页的空间不够,会分裂页,此时会比较消耗性恶NGN
3.定长字符神奇的空格问题
-- char类型,string3末尾的空格莫名的不见了
create table char_test( char_col char(10));
insert into char_test(char_col) values ('string1'),(' string2'),('string3 ');
select * from char_test;
select concat("'",char_col,"'") from char_test;
-- 换成varchar类型,就不会出现这个问题
drop table char_test;
create table char_test( char_col varchar(10));
insert into char_test(char_col) values ('string1'),(' string2'),('string3 ');
select concat("'",char_col,"'") from char_test;
执行记录:
mysql> create table char_test( char_col char(10));
Query OK, 0 rows affected (0.04 sec)
mysql> insert into char_test(char_col) values ('string1'),(' string2'),('string3 ');
Query OK, 3 rows affected (0.02 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from char_test;
+------------+
| char_col |
+------------+
| string1 |
| string2 |
| string3 |
+------------+
3 rows in set (0.00 sec)
mysql> select concat("'",char_col,"'") from char_test;
+--------------------------+
| concat("'",char_col,"'") |
+--------------------------+
| 'string1' |
| ' string2' |
| 'string3' |
+--------------------------+
3 rows in set (0.00 sec)
mysql>
mysql>
mysql> drop table char_test;
Query OK, 0 rows affected (0.03 sec)
mysql> create table char_test( char_col varchar(10));
Query OK, 0 rows affected (0.02 sec)
mysql> insert into char_test(char_col) values ('string1'),(' string2'),('string3 ');
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select concat("'",char_col,"'") from char_test;
+--------------------------+
| concat("'",char_col,"'") |
+--------------------------+
| 'string1' |
| ' string2' |
| 'string3 ' |
+--------------------------+
3 rows in set (0.00 sec)
BLOB和TEXT类型
类型 | 描述 |
---|---|
TINYBLOB | 最大长度255(2^8-1),使用1字节前缀存储长度信息 |
BLOB | 最大长度65,535(2^16-1),使用2字节前缀存储长度信息 |
MEDIUMBLOB | 最大长度16,777,215(2^24-1),使用3字节前缀存储长度信息 |
LONGBLOB | 最大长度(2^32-1)或4GB,使用4字节前缀存储长度信息 |
TINYTEXT | 最大长度255(2^8-1),使用1字节前缀存储长度信息 |
TEXT | 最大长度65,535(2^16-1),使用2字节前缀存储长度信息 |
MEDIUMTEXT | 最大长度16,777,215(2^24-1),使用3字节前缀存储长度信息 |
LONGTEXT | 最大长度(2^32-1)或4GB,使用4字节前缀存储长度信息 |
BLOB是SMALLBLOB的同义词
TEXT是SMALLTEXT的同义词
MySQL把每个BLOB和TEXT当做一个独立的对象处理。
当BLOB和TEXT值太大时,InnoDB会使用专门的外部存储区域来进行存储,此时每个值在行内需要1-4个值存储一个指针,然后在外部存储区域存储实际的值
BLOB和TEXT家族之间仅有的不同是BLOB类型存储的是二进制数据,没有排序规则或字符集,而TEXT类型有字符集和排序规则。
ENUM和SET类型
类型 | 描述 |
---|---|
ENUM(‘v1’,‘v2’…) | 一个enum最多可包含65,535个不同的元素。单个ENUM元素的最大支持长度是255(文字长度)。枚举值内部对应一个索引,从1开始。 |
SET(‘v1’,‘v2’…) | 一个SET列最多可包含64个不同的元素。单个SET元素的最大支持长度是255(文字长度) |
CREATE TABLE test_enum_set (
a SET('A','B','C'),
b ENUM('张三','李四')
);
insert into test_enum_set values ('A','张三');
insert into test_enum_set values ('A,B','张三');
insert into test_enum_set values ('A,B','张三,李四');
执行记录:
mysql> CREATE TABLE test_enum_set (
-> a SET('A','B','C'),
-> b ENUM('张三','李四')
-> );
Query OK, 0 rows affected (0.03 sec)
mysql> insert into test_enum_set values ('A','张三');
Query OK, 1 row affected (0.01 sec)
mysql> insert into test_enum_set values ('A,B','张三');
Query OK, 1 row affected (0.01 sec)
mysql> insert into test_enum_set values ('A,B','张三,李四');
ERROR 1265 (01000): Data truncated for column 'b' at row 1
mysql>
mysql> select * from test_enum_set;
+------+--------+
| a | b |
+------+--------+
| A | 张三 |
| A,B | 张三 |
+------+--------+
2 rows in set (0.00 sec)