MySQL基础篇(2)数据类型
MySQL提供了多种数据类型,主要包括数值型、字符串类型、日期和时间类型。
1.数值类型
- 整数类型:TINYINT(1字节)、SMALLINT(2字节)、MEDIUMINT(3字节)、INT(INTEGER)(4字节)、BIGINT(8字节)
- 浮点类型:FLOAT(4字节)、DOUBLE(8字节)
- 定点数类型:DEC(M,D)(M + 2字节)、DECIMAL(M,D)(M+2字节)
- 位类型:BIT(M)(1~8字节)
(1)整数类型
MySQL支持在类型名称后面的小括号内指定显示宽度,例如int(5)表示当数值宽度小于5位的时候在数字前面填满宽度,如果指示显示宽度则默认为int(11)。一般配合zerofill使用,zerofill就是用‘0’填充的意思,也就是在数字位数不够的空间用字符‘0’填满。
mysql> create table t1(id1 int, id2 int(5)); Query OK, 0 rows affected (0.01 sec) mysql> desc t1; +-------+---------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+---------+------+-----+---------+-------+ | id1 | int(11) | YES | | NULL | | | id2 | int(5) | YES | | NULL | | +-------+---------+------+-----+---------+-------+ 2 rows in set (0.00 sec) mysql> insert into t1 values(1,1); Query OK, 1 row affected (0.01 sec) mysql> select * from t1; +------+------+ | id1 | id2 | +------+------+ | 1 | 1 | +------+------+ 1 row in set (0.00 sec) mysql> alter table t1 modify id1 int zerofill; Query OK, 1 row affected (0.06 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql> alter table t1 modify id2 int(5) zerofill; Query OK, 1 row affected (0.06 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql> select * from t1; +------------+-------+ | id1 | id2 | +------------+-------+ | 0000000001 | 00001 | +------------+-------+ 1 row in set (0.00 sec) mysql> insert into t1 values(1,1111111); Query OK, 1 row affected (0.01 sec) mysql> select * from t1; +------------+---------+ | id1 | id2 | +------------+---------+ | 0000000001 | 00001 | | 0000000001 | 1111111 | +------------+---------+ 2 rows in set (0.00 sec)
如果插入大于宽度限制的值,例如int(5)的属性值插入为1111111,那么此时显示宽度将没有意义,而是按照实际的精度进行保存。
UNSIGNED:所有的整数类型都有一个可选属性UNSIGNED(无符号),如果需要在字段里面保存非负数或者较大的上限值时,可以使用此选项。例如,tinyint(1字节)的有符号的范围是-128~+127,而无符号的范围就是0~255。如果一个列指定为zerofill,则MySQL自动为该列添加UNSIGNED属性。
AUTO_INCREMENT:在需要产生唯一标识符或顺序值时,可以使用此属性,这个属性只用于整数类型。AUTO_INCREMENT值一般从1开始,每行增加1。一个表中最多只能有一个AUTO_INCREMENT列,同时该列应该定义为NOT NULL,并定义为PRIMARY KEY或者UNIQUE键。
CREATE TABLE AI(ID INT AUTO_INCREMENT NOT NULL PRIMARY KEY); CREATE TABLE AI(ID INT AUTO_INCREMENT NOT NULL, PRIMARY KEY(ID)); CREATE TABLE AI(ID INT AUTO_INCREMENT NOT NULL, UNIQUE(ID));
(2)小数类型:浮点数和定点数
浮点数包括float(单精度)、double(双精度),
定点数则只有decimal一种表示,顶点数在MySQL内部是以字符串形式存放,比浮点数更精确,适合用来表示货币等精度高的数据。
浮点数和定点数都可以用类型名称后加“(M,D)”来表示该值一共显示M位数字(整数位+小数位),其中D位位于小数点后面,M和D又称为精度和标度。
例如,定义为float(7,4)的一个列可以显示为-999.9999,而如果要插入999.00009,则会四舍五入近似结果为999.0001
float和double在不指定精度时,默认会按照实际的精度来显示,而decimal在不指定精度时,默认的整数位为10,默认的小数位为0
假设在float,double,decimal中插入1.234,1.234,1.234,则前两个会按照实际精度值显示成1.234,而由于最后一个默认decimal(10,0)就会显示成1
假设在float(5,2),double(5,2),decimal(5,2)中插入1.234,1.234,1.234,则前两个数会四舍五入为1.23,最后一个虽然会报警,然而还是按照实际精度四舍五入后插入。
假设在float(10,2),decimal(10,2)中插入131072.32,131072.32后前一个数会插入131072.31,后一个数会插入131072.32,这是因为使用单精度浮点数产生了误差。
如何选择合适的数据类型:
使用浮点型数据保存小数时,会有可能产生误差。在精度要求比较高的应用中(比如货币)要使用定点数而不是浮点数来保存数据。
(3)位类型
位类型可以用于存放字段值,BIT(M)可以用来存放多位二进制数,M范围从1~64,如果不写则默认为1位。对于位字段,直接使用select命令将不会看到结果,可以用bin()显示为二进制格式或者hex()显示为十六进制格式函数进行读取。
数据插入bit类型字段时,首先转换为二进制,如果位数允许,将成功插入;如果位数超过定义的位数,则插入失败。例如bit默认类型插入2会插入失败,因为2转换成二进制后是“10”,而bit默认位数为1,将bit修改成bit(2)即可插入。
mysql> create table t3(id bit); Query OK, 0 rows affected (0.02 sec) mysql> insert into t3 values(1); Query OK, 1 row affected (0.01 sec) mysql> select * from t3; +------+ | id | +------+ | | +------+ 1 row in set (0.00 sec) mysql> select bin(id),hex(id) from t3; +---------+---------+ | bin(id) | hex(id) | +---------+---------+ | 1 | 1 | +---------+---------+ 1 row in set (0.00 sec) mysql> insert into t3 value(2); ERROR 1406 (22001): Data too long for column 'id' at row 1 mysql> desc t3; +-------+--------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+--------+------+-----+---------+-------+ | id | bit(1) | YES | | NULL | | +-------+--------+------+-----+---------+-------+ 1 row in set (0.00 sec) mysql> alter table t3 modify id bit(2); Query OK, 1 row affected (0.07 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql> insert into t3 value(2); Query OK, 1 row affected (0.01 sec) mysql> select bin(id),hex(id) from t3; +---------+---------+ | bin(id) | hex(id) | +---------+---------+ | 1 | 1 | | 10 | 2 | +---------+---------+ 2 rows in set (0.00 sec)
2.日期时间类型
- DATE(4字节):表示年月日,1000-01-01 ~ 9999-12-31
- DATETIME(8字节):表示年月日时分秒,1000-01-01 00:00:00 ~ 9999-12-31 23:59:59
- TIME(3字节):表示时分秒,-838:59:59 ~ 838:59:59
- TIMESTAMP(4字节):需要经常插入或者更新日期为当前系统时间,19700101080001 ~ 2038年的某个时刻,但是查询显示的结果是类似“0000-00-00 00:00:00”的字符串
- YEAR(1字节):表示年份,默认是4位格式,1901 ~ 2155
TIMESTAMP和DATETIME的区别:
- TIMESTAMP支持的时间范围很小,DATATIME范围更大。
- 表中的第一个TIMESTAMP列自动设置为系统时间,如果在一个TIMESTAMP中插入null,则该列值也会自动设置该列的值为当前的时间;在插入或更新一行但不明确给TIMESTAMP赋值时也会自动设置该列的值为当前的日期和时间,当插入的值超出取值范围时,用“0000-00-00 00:00:00”填充。
- TIMESTAMP的插入和查询都受当地时区的影响,更能反映出实际的日期,而DATATIME则只能反映出插入时当地的时区。
日期类型的插入格式有很多种,下面的几种都是可以的(假设create table t6(dt datetime);):
- 严格语法的字符串:insert into t6 values('2007-9-3 12:10:10');
- 不严格语法的字符串:insert into t6 value('2007!9@3 12$10%10');
- 没有间隔的数字字符串:insert into t6 value('20070903121010');
- 纯数字:insert into t6 value(20070903121010);
- 函数返回的结果:insert into t6 value(now());
3.字符串类型
MySQL中提供了多种对字符数据的存储类型
(1)CHAR和VARCHAR类型
都用来保存MySQL中较短的字符串,二者的主要区别在于存储方式的不同:
CHAR列的长度固定位创建表时声明的长度,长度可以从0~255的任何值;而VARCHAR列中的值为可变长字符串,长度可以指定为0~255或者65536之间的值。
在检索的时候,CHAR列删除了尾部的空格,而VARCHAR则保留了这些空格。
如何选择合适的数据类型:
CHAR属于固定长度的字符类型,而VARCHAR属于可变长度的字符类型。
由于CHAR是固定长度的,所以它的处理速度比VARCHAR快得多,但是其缺点是浪费存储空间,程序需要对行尾空格进行处理,所以对于那些长度变化不大并且对查询速度有较高要求的数据可以考虑使用CHAR类型来存储。
对于不同类型的存储引擎来说:
- MyISAM和MEMORY:使用固定长度的数据列代替可变长度的数据列
- InnoDB:使用VARCHAR类型。(行存储格式不区分固定和可变,并且存储容量和磁盘I/O比较好)
(2)BINARY和VARBINARY类型
它们包含二进制字符串而不包含非二进制字符串。在保存BINARY值时,在值的最后通过填充“0x00”以达到指定的字段定义长度。
(3)ENUM类型
它的值范围需要在创建表时通过枚举方式显示指定,对1~255个成员的枚举需要1个字节存储;对于255~65535个成员,需要2个字节存储。最多允许65535个成员。
创建测试表t,定义gender字段为枚举类型,成员为'M'和'F',由结果可以看到,ENUM是忽略大小写的,当插入在'M'和'F'中间的某个值('M')和('f')时,可以成功插入,而插入一个不在ENUM成员中的值时,并没有返回警告也不会报错,而是插入了ENUM中的第一个值'M'。
mysql> create table t(gender enum('M','F')); Query OK, 0 rows affected (0.01 sec) mysql> select * from t; Empty set (0.00 sec) mysql> insert into t values('M'),('1'),('f'),(NULL); Query OK, 4 rows affected (0.00 sec) Records: 4 Duplicates: 0 Warnings: 0 mysql> select * from t; +--------+ | gender | +--------+ | M | | M | | F | | NULL | +--------+ 4 rows in set (0.00 sec)
(4)SET类型
SET和ENUM类型非常类似,也是一个字符串对象,里面可以包含0~64个成员。
根据成员数量的不同,分为1~8(1字节)、9~16(2字节)、17~24(3字节)、25~32(4字节)、33~64(8字节)
SET和ENUM最主要的区别就是SET类型一次可以选取多个成员,而ENUM只能选取一个。
对于在set中的值,都可以被成功地插入,如果不在,就会报错;对于重复的对象,例如('a,d,a')在插入之后会去重变成“a,d”。
mysql> create table setTest(col set('a','b','c','d')); Query OK, 0 rows affected (0.03 sec) mysql> insert into setTest values('a,b'),('a,d,a'),('a,b'),('a,c'),('a'),('a,e'),('e'); ERROR 1265 (01000): Data truncated for column 'col' at row 6 mysql> insert into setTest values('a,b'),('a,d,a'),('a,b'),('a,c'),('a'),('a,e'); ERROR 1265 (01000): Data truncated for column 'col' at row 6 mysql> insert into setTest values('a,b'),('a,d,a'),('a,b'),('a,c'),('a'); Query OK, 5 rows affected (0.01 sec) Records: 5 Duplicates: 0 Warnings: 0 mysql> select * from setTest; +------+ | col | +------+ | a,b | | a,d | | a,b | | a,c | | a | +------+ 5 rows in set (0.00 sec)
(5)TEXT与BLOB
一般在保存少量字符串的时候,会选择CHAR或者VARCHAR;而在保存较大文本时,通常会选择使用TEXT或BLOB。
区别就是BLOB可以用来保存二进制数据,比如照片;而TEXT只能保存字符数据,比如一篇文章或者日记。
TEXT又分为TEXT、MEDIUMTEXT、LONGTEXT,BLOB又分为BLOB、MEDIUMBLOB、LONGBLOB。有一些常见问题:
- BLOB和TEXT值会引起一些性能问题,特别是在执行了大量的删除操作时。删除操作会在数据表中留下很大的“空洞”,以后填入这些“空洞”的记录在插入的性能上会有影响。为了提高性能,建议定期使用[OPTIMIZE TABLE 表名]功能对这类表进行碎片整理,避免因为“空洞”导致性能问题。
- 可以使用合成的(Synthetic)索引来提高BLOB或TEXT的查询性能。合成索引就是根据大文本字段的内容建立一个散列值,并把这个值存储在单独的数据列中,查询的时候就可以通过检索散列值定位到数据了。这种技术只能用于精确匹配,在一定从程度上减少了I/O,提高了查询效率。如果需要对BLOB字段进行模糊查询,就可以使用前缀索引,也就是只为字段的前n列创建索引,例如create index idx on t(context(100))就是为字段的前100个字符创建索引,然后使用like进行模糊查询即可。
mysql> create table t(id varchar(100), context blob, hash_value varchar(40)); Query OK, 0 rows affected (0.05 sec) mysql> insert into t values(1,repeat('beijing',2),md5(context)); Query OK, 1 row affected (0.03 sec) mysql> insert into t values(2,repeat('beijing',2),md5(context)); Query OK, 1 row affected (0.01 sec) mysql> insert into t values(3,repeat('beijing_2008',2),md5(context)); Query OK, 1 row affected (0.03 sec) mysql> select * from t; +------+--------------------------+----------------------------------+ | id | context | hash_value | +------+--------------------------+----------------------------------+ | 1 | beijingbeijing | 09746eef633dbbccb7997dfd795cff17 | | 2 | beijingbeijing | 09746eef633dbbccb7997dfd795cff17 | | 3 | beijing_2008beijing_2008 | ab35be94cba8e0d4c51740c5d21640d4 | +------+--------------------------+----------------------------------+ 3 rows in set (0.00 sec) mysql> select * from t where hash_value=md5(repeat('beijing_2008',2)); +------+--------------------------+----------------------------------+ | id | context | hash_value | +------+--------------------------+----------------------------------+ | 3 | beijing_2008beijing_2008 | ab35be94cba8e0d4c51740c5d21640d4 | +------+--------------------------+----------------------------------+ 1 row in set (0.00 sec)
mysql> create index idx_blob on t(context(100)); Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> select * from t where context like 'beijing%'; +------+--------------------------+----------------------------------+ | id | context | hash_value | +------+--------------------------+----------------------------------+ | 1 | beijingbeijing | 09746eef633dbbccb7997dfd795cff17 | | 2 | beijingbeijing | 09746eef633dbbccb7997dfd795cff17 | | 3 | beijing_2008beijing_2008 | ab35be94cba8e0d4c51740c5d21640d4 | +------+--------------------------+----------------------------------+ 3 rows in set (0.00 sec) mysql> desc select * from t where context like 'beijing%' \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t partitions: NULL type: ALL possible_keys: idx_blob key: NULL key_len: NULL ref: NULL rows: 3 filtered: 100.00 Extra: Using where 1 row in set, 1 warning (0.00 sec)
- 在不必要的时候避免检索大型的BLOB或TEXT值。select * 直接查询就不是很好的想法,除非能够确定作为约束条件的WHERE字句只会找到所需要的数据行。
- 把BLOB或TEXT列分离到单独的表中。如果把数据列移动到第二章数据表中,可以把原数据表中的数据列转换成固定长度的数据行格式,这会减少主表中的碎片,可以得到固定长度数据行的性能优势,还可以是主数据表在执行select * 查询的时候不会通过网络传输大量的BLOB或TEXT值。