学习了mysql表的常用操作,系统学习下数据类型。
1,数据类型
MySQL支持多种数据类型,主要有数值类型、日期/时间类型和字符串类型。
(1)数值数据类型:包括整数类型TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT、浮点小数数据类型 FLOAT和 DOUBLE,定点小数类型 DECIMAL。
(2)日期/时间类型:包括YEAR、TIME、DATE、DATETIME和TIMESTAMP。
(3)字符串类型:包括CHAR、VARCHAR、BINARY、VARBINARY、BLOB、TEXT、ENUM和 SET等。字符串类型又分为文本字符串和二进制字符串。
1,整数类型
数值型数据类型主要用来存储数字,MySQL提供了多种数值数据类型,不同的数据类型提供不同的取值范围,可以存储的值范围越大,其所需要的存储空间也会越大。MySQL主要提供的整数类型有:TINYINT、SMALLINT、MEDIUMINT、INT(INTEGER)、BIGINT。整数类型的属性字段可以添加AUTO_INCREMENT自增约束条件。表1列出了MySQL 中的数值类型。
表1 MySQL中的整数型数据类型
|--------------|---------|------|
| 类型 | 说明 | 存储需求 |
| TINYINT | 很小的整数 | 1个字节 |
| SMALLINT | 小的整数 | 2个字节 |
| MEDIUMINT | 中等大小的整数 | 3个字节 |
| INT(INTEGER) | 普通大小的整数 | 4个字节 |
| BIGINT | 大整数 | 8个字节 |
从表中可以看到,不同类型整数存储所需的字节数是不同的,占用字节数最小的是TINYINT类型,占用字节最大的是 BIGINT类型,相应的占用字节越多的类型所能表示的数值范围越大。根据占用字节数可以求出每一种数据类型的取值范围,例如TINYINT需要1个字节(8 bits)来存储,那么TINYINT无符号数的最大值为2^8-1,即255;TINYINT有符号数的最大值为2^7-1,即127。其他类型的整数的取值范围计算方法相同,如表2所示。
|--------------|-------------------------------------------|-------------------------|
| 数据类型 | 有符号 | 无符号 |
| TINYINT | -128~127 | 0~255 |
| SMALLINT | 32768~32767 | 0~65535 |
| MEDIUMINT | -8388608~8388607 | 0~16777215 |
| INT(INTEGER) | -2147483648~2147483647 | 0~4294967295 |
| BIGINT | -9223372036854775808~9223372036854775807 | 0~18446744073709551615 |
有如下创建表的语句:
sql
CREATE TABLE tb_emp1
(
id INT(11),
name VARCHAR(25),
dept_id INT(11),
salary FLOAT
);
id字段的数据类型为INT(11),注意到后面的数字11,这表示的是该数据类型指定的显示宽度,指定能够显示的数值中数字的个数。例如,假设声明一个INT类型的字段:
year INT(4)
该声明指明,在year字段中的数据一般只显示4位数字的宽度。
在这里要注意:显示宽度和数据类型的取值范围是无关的。显示宽度只是指明MySQL最大可能显示的数字个数,数值的位数小于指定的宽度时会由空格填充;如果插入了大于显示宽度的值,只要该值不超过该类型整数的取值范围,数值依然可以插入,而且能够显示出来。例如,假如向year字段插入一个数值19999,当使用SELECT查询该列值的时候,MySQL 显示的将是完整的带有5位数字的19999,而不是4位数字的值。
其他整型数据类型也可以在定义表结构时指定所需要的显示宽度,如果不指定,则系统为每一种类型指定默认的宽度值,如例1所示。
【例1】创建表 tmpl,其中字段x、y、z、m、n数据类型依次为TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT,SQL语句如下:
sql
CREATE TABLE tmp1 (
x TINYINT,
y SMALLINT,
z MEDIUMINT,
m INT,
n BIGINT
)
可以看到,系统将添加不同的默认显示宽度。这些显示宽度能够保证显示每一种数据类型可以取到取值范围内的所有值。例如TINYINT有符号数和无符号数的取值范围分别为-128~127和0~255,由于负号占了一个数字位,因此 TINYINT 默认的显示宽度为4。同理,其他整数类型的默认显示宽度与其有符号数的最小值的宽度相同。
不同的整数类型有不同的取值范围,并且需要不同的存储空间,因此,应该根据实际需要选择最合适的类型,这样有利于提高查询的效率和节省存储空间。整数类型是不带小数部分的数值,现实生活中很多地方需要用到带小数的数值,下面将介绍MySQL中支持的小数类型。
显示宽度只用于显示,并不能限制取值范围和占用空间,如:INT(3)会占用4个字节的存储空间,并且允许的最大值也不会是999,而是INT整型所允许的最大值。
2,浮点数类型和定点数类型
MySQL中使用浮点数和定点数来表示小数。浮点类型有两种:单精度浮点类型(FLOAT)和双精度浮点类型(DOUBLE)。定点类型只有一种:DECIMAL。浮点类型和定点类型都可以用(M,N)来表示,其中M称为精度,表示总共的位数;N称为标度,是表示小数的位数。表3列出了MySQL中的小数类型和存储需求。
|-------------------|-----------|--------|
| 类型 | 说明 | 存储需求 |
| FLOAT | 单精度浮点数 | 4个字节 |
| DOUBLE | 双精度浮点数 | 8个字节 |
| DECIMAL (M,D),DEC | 缩的"严格"定点数 | M+2个字节 |
DECIMAL类型不同于FLOAT和 DOUBLE,DECIMAL 实际是以串存放的,DECIMAL可能的最大取值范围与 DOUBLE 一样,但是其有效的取值范围由M和D的值决定。如果改变M而固定D,则其取值范围将随M的变大而变大。从表3可以看到,DECIMAL的存储空间并不是固定的,而由其精度值M决定,占用M+2个字节。
FLOAT类型的取值范围如下:
·有符号的取值范围:-3.402823466E+38~-1.175494351E-38。
·无符号的取值范围:0和1.175494351E-38~3.402823466E+38。
DOUBLE类型的取值范围如下:
·有符号的取值范围:-1.7976931348623157E+308~-2.2250738585072014E-308。
·无符号的取值范围:0和2.2250738585072014E-308~1.7976931348623157E+308。
不论是定点还是浮点类型,如果用户指定的精度超出精度范围,则会四舍五入进行处理。
【例2】创建表tmp2,其中字段x、y、z数据类型依次为FLOAT(5,1)、DOUBLE(5,1)和DECIMAL(5,1),向表中插入数据5.12、5.15和5.123,SQL语句如下:
sql
CREATE table tmp2 (
x DECIMAL(5,1),
y DECIMAL(5,1),
z DECIMAL(5,1)
);
向表中插入数据:
sql
INSERT INTO tmp2 VALUES (5.12, 5.15, 5.123);
可以看到在插入数据时,MySQL给出了一个警告信息,使用SHOW WARNINGS;语句查看警告信息:
mysql SHOW WARN Data truncated for column 'z' at row 1
可以看到FLOAT 和 DOUBLE在进行四舍五入时没有给出警告,而给出z字段数值被截断的警告。查看结果:
FLOAT和 DOUBLE 在不指定精度时,默认会按照实际的精度(由计算机硬件和操作系统决定),DECIMAL 如不指定精度,默认为(10,0)。
浮点数相对于定点数的优点是在长度一定的情况下,浮点数能够表示更大的数据范围;它的缺点是会引起精度问题。
提示:在MySQL中,定点数以字符串形式存储,在对精度要求比较高的时候(如货币,科学数据等)使用DECIMAL的类型比较好,另外两个浮点数进行减法和比较运算时也容易出问题,所以在使用浮点型时需要注意,并尽量避免做浮点数比较。
3,日期与时间类型
MySQL中有多种表示日期的数据类型,主要有:DATETIME、DATE、TIMESTAMP、TIME和 YEAR。例如,当只记录年信息的时候,可以只使用 YEAR类型,而没有必要使用DATE。每一个类型都有合法的取值范围,当指定确实不合法的值时系统将"零"值插入到数据库中、表4列出了MySQL 中的日期与时间类型。
表4日期与时间数据类型
|-----------|-------------|------------------------------------------------------------------------------------------------------------|---------------------|--------------|
| 类型 | 大小 ( bytes) | 范围 | 格式 | 用途 |
| 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 结束时间是第 2147483647 秒,北京时间 2038-1-19 11:14:07,格林尼治时间 2038年1月19日 凌晨 03:14:07 | YYYYMMDD HHMMSS | 混合日期和时间值,时间戳 |
1,YEAR
YEAR类型是一个单字节类型用于表示年,在存储时只需要1个字节。可以使用各种格式指定YEAR值,如下所示:
(1)以4位字符串或者4位数字格式表示的YEAR,范围为'1901'~~'2155'。输入格式为'YYYY'或者YYYY,例如,输入'2010'或2010,插入到数据库的值均为2010。
(2)以2位字符串格式表示的YEAR,范围为'00'到'99'。'00'~'69'和'70'~'99'范围的值分别被转换为2000~2069和1970~1999范围的YEAR值。'0'与'00'的作用相同。插入超过取值范围的值将被转换为2000。
(3)以2位数字表示的YEAR,范围为1~99。1~69和70~99范围的值分别被转换为2001~2069和1970~1999范围的YEAR值。注意:在这里0值将被转换为0000,而不是2000。
提示:两位整数范围与两位字符串范围稍有不同,例如:插入2000年,读者可能会使用数字格式的0表示YEAR,实际上,插入数据库的值为0000,而不是所希望的2000。只有使用字符串格式的0'或'00',才可以被正确地解释为2000。非法 YEAR值将被转换为0000。
【例3】创建数据表tmp3,定义数据类型为YEAR的字段y,向表中插入值2010,'2010','2166',SQL语句如下:
首先创建表tmp3:
sql
CREATE TABLE tmp3(y YEAR);
向表中插入数据:
sql
INSERT INTO tmp3 values(2010), ('2010');
再次向表中插入数据:
sql
INSERT INTO tmp3 values ('2166');
ERROR 1264 (22003):Out of range value for column 'y' at row 1
语句执行之后,MySQL给出了一条错误提示,使用SHOW查看错误信息
sql
SHOW WARNINGS;
可以看到,插入的第3个值2166超过了YEAR类型的取值范围,此时不能正常的执行插入操作,查看结果:
sql
SELECT * FROM tmp3
由结果可以看到,当插入值为数值类型的2010或者字符串类型的'2010'时,都正确地储存到了数据库中;而当插入值'2166'时,由于超出了YEAR类型的取值范围,因此,不能插入值。
【例4】向tmp3表中y字段插入2位字符串表示的YEAR值,分别为'0'、 '00'、'77'和'10',SQL语句如下:
首先删除表中的数据:
sql
DELETE FROM tmp3;
向表中插入数据:
sql
INSERT INTO tmp3 values('0'), ('00'), ('77'), ('10');
查看结果:
sql
SELECT * FROM tmp3;
由结果可以看到,字符串'0'和'00'的作用相同,分别都转换成了2000年;'77'转换为1977;'10'转换为2010。
【例5】向tmp3表中y字段插入2位数字表示的YEAR值,分别为0、78和11,SQL语句如下:
首先删除表中的数据:
sql
DELETE FROM tmp3;
向表中插入数据;
sql
INSERT INTO tmp3 values(0), (78), (11);
查看结果:
sql
SELECT * FROM tmp3;
由结果可以看到,0被转换为0000;78被转换为1978;11被转换为2011。
2,TIME
TIME类型用于只需要时间信息的值,在存储时需要3个字节。格式为'HH:MM:SS'。HH表示小时;MM表示分钟;SS表示秒。TIME类型的取值范围为-838:59:59~838:59:59,小时部分会如此大的原因是TIME类型不仅可以用于表示一天的时间(必须小于24小时),还可能是某个事件过去的时间或两个事件之间的时间间隔(可以大于24小时,或者甚至为负)。可以使用各种格式指定TIME值,如下所示:
(1)'D HH:MM:SS'格式的字符串。还可以使用下面任何一种"非严格"的语法:'HH:MM:SS'、'HH:MM'、 'D HH:MM'、'DHH'或'SS'。这里的D表示日,可以取0~34之间的值。在插入数据库时,D被转换为小时保存,格式为"D*24+HH"。
(2)'HHMMSS'格式的、没有间隔符的字符串或者HHMMSS格式的数值,假定是有意义的时间。例如:'101112'被理解为'10:11:12',但'109712'是不合法的(它有一个没有意义的分钟部分),存储时将变为00:00:00。
注意:为TIME 列分配简写值时应注意:如果没有冒号,MySQL解释值时,假定最右边的两位表示秒。(MySQL解释TIME值为过去的时间而不是当天的时间)。例如,读者可能认为'1112'和1112表示11:12:00(即 11点过12分),但MySQL将它们解释为00:11:12(即11分12秒)。同样'12'和12被解释为00:00:12。相反,TIME值中如果使用冒号则肯定被看作当天的时间。也就是说,'11:12'表示11:12:00,而不是00:11:12。
【例6】创建数据表tmp4,定义数据类型为TIME 的字段t,向表中插入'10:05:05','23:23', '2 10:10','3 02','10',SQL语句如下:
首先创建表tmp4,
sql
CREATE TABLE tmp4(t TIME );
向表中插入数据:
sql
INSERT INTO tmp4 values ( '10:05:05 '), ('23:23'), ('2 10:10'),
('3 02'), ('10');
查看结果:
sql
SELECT * FROM tmp4;
由结果可以看到,'10:05:05'被转换为 10:05:05,
'23:23'被转换为23:23:00;
'2 10:10'被转换为58:10:00,
'3 02'被转换为74:00:00;
'10'被转换成00:00:10.
提示:在使用'DHH'格式时,小时一定要使用双位数值,如果是小于10的小时数,应在前面加0。
【例7】向表tmp4中插入值'101112',111213,'0', 107010,SQL语句如下:首先删除表中的数据:
sql
DELETE FROM tmp4;
向表中插入数据:
sql
INSERT INTO tmp4 values ('101112'), (111213), ('0');
再向表中插入数据:
sql
INSERT INTO tmp4 values (107010);
可以看到,在插入数据时,MySQL给出了一个错误提示信息,使用SHOW WARNINGS;
查看错误信息,如下所示:
Incorrect time value:"107010'for column 't' at row 1
可以看到,第二次在插入记录的时候,数据超出了范臣,原因是107010的分钟部分超过了60,分钟部分是不会超过60的,查看结果:
sql
SELECT * FROM tmp4;
由结果可以看到,
'101112'被转换为10:11:12;
111213被转换为11:12:13:"
0'被转换为00:00:00:
107010因为是不合法的值,因此不能被插入。
也可以使用系统日期函数向TIME字段列插入值。
【例8】向tmp4表中插入系统当前时间,SQL语句如下:
首先删除表中的数据:
sql
DELETE FROM tmp4;
向表中插入数据:
sql
INSERT INTO tmp4 values (CURRENT_TIME), (NOW());
查看插入结果:
sql
SELECT * FROM tmp4;
由结果可以看到,获取系统当前的日期时间插入到TIME类型列,因为输入语句的时间不确定,因此获取的值与这里的可能是不同的,但都是系统当前的日期时间值。
3,DATE类型
DATE类型用在仅需要日期值时,没有时间部分,在存储时需要3个字节。日期格式为'YYYY-MM-DD'其中YYYY表示年;MM表示月;DD表示日。在给DATE 类型的字段赋值时,可以使用字符串类型或者数字类型的数据插入,只要符合DATE的日期格式即可,如下:
(1)以'YYYY-MM-DD'或者'YYYYMMDD'字符串格式表示的日期,取值范围为'1000-01-0l'~'9999-12-3'。例如,输入'2012-12-31'或者'20121231',插入数据库的日期都为2012-12-31。
(2)以'YY-MM-DD'或者'YYMMDD'字符串格式表示的日期,在这里YY表示两位的年值。包含两位年值的日期会令人模糊,因为不知道世纪。MySQL 使用以下规则解释两位年值:'00~69'范围的年值转换为'2000~2069';'70~99'范困的年值转换为'1970~1999'。例如,输入'12-12-31',插入数据库的日期为2012-12-31:输入'981231',插入数据的日期为1998-12-31。
(3)以 YY-MM-DD 或者YYMMDD 数字格式表示的日期,与前面相似,00-69范围的年值转换为2000~2069:70~99范围的年值转换为1970~1999。例如,输入12-12-31插入数据库的日期为2012-12-31:输入981231,插入数据的日期为1998-12-31。
(4)使用CURRENT_DATE或者NOwO,插入当前系统日期。
【例9】创建数据表tmp5,定义数据类型为 DATE的字段d,向表中插入"YYYY-MM-DD"和"YYYYMMDD"字符串格式日期,
SQL语句如下:
首先创建表tmp5:
sql
CREATE TABLE tmp5 (d DATE);
向表中插入"YYYY-MM-DD"和"YYYYMMDD"格式的日期:
sql
INSERT INTO tmp5 VALUES ('1998-08-08'), ('19980808'), ('120101010');
查看插入结果:
sql
SELECT * FROM tmp5;
可以看到,各个不同类型的日期值都正确地插入到了数据表中。
【例10】向tmp5表中插入"YY-MM-DD"和"YYMMDD"字符串格式的日期,SQL语句如下:
首先删除表中的数据:
sql
DELETE FROM tmp5;
向表中插入"YY-MM-DD"和"YYMMDD"格式日期;
sql
INSERT INTO tmp5 values ('99-09-09'), ('990909'),('000101'), ('111111');
查看插入结果:
sql
SELECT* FROM tmp5;
【例11】向tmp5表中插入YY-MM-DD和YYMMDD 数字格式日期,SQL语句如下:
首先删除表中的数据:
sql
DELETE FROM tmp5;
向表中插入YY-MM-DD和YYMMDD数字格式日期:
sql
INSERT INTO tmp5 values ('99-09-09'), ('990909'),('000101') ,('111111');
查看插入结果:
sql
SELECT *FROM tmp5;
【例12】向tmp5表中插入系统当前日期,SQL语句如下:
首先删除表中的数据:
sql
DELETE FROM tmp5;
向表中插入系统当前日期:
sql
INSERT INTO tmp5 values(CURRENT_DATE()), (NOW());
查看插入结果:
sql
SELECT* FROM tmp5;
CURRENT_DATE 只返回当前日期值,不包括时间部分;
NOW()函数返回日期和时间值,在保存到数据库时,只保留了其日期部分。
提示:MySQL允许"不严格"语法:任何标点符号都可以用作日期部分之间的间隔符。例如,'98-11-31'、'98.11.31'、'98/11/31'和'98@11@31'是等价的,这些值也可以正确地插入到数据库。
4,DATETIME
DATETIME类型用在需要同时包含日期和时间信息的值,在存储时需要8个字节。日期格式为'YYYY-MM-DD HH:MM:SS',其中 YYYY表示年;MM 表示月;DD表示日;HH表示小时。MM表示分钟;SS表示秒。在给 DATETIME 类型的字段赋值时,可以使用字符串类型或者数字类型的数据插入,只要符合DATETIME的日期格式即可,如下所示:
(1)以'YYYY-MM-DD HH;MM:SS'或者'YYYYMMDDHHMMSS'字符串格式表示的值,取值范围为'1000-01-01 00:00:00'~'9999-12-3 23:59:59'。例如输入'2012-12-31 05:05:05'或者'20121231050505',插入数据库的DATETIME值都为2012-12-31 05:05:05.
(2)以'YY-MM-DD HH:MM:SS'或者'YYMMDDHHMMSS'字符串格式表示的日期,在这里YY表示两位的年值。与前面相同,"00~69'范围的年值转换为'2000~2069';'70~99'范i的年值转换为'1970~1999'。例如输入'12-12-31 05:05:05',插入数据库的DATETIME 为2012-12-31 05:05:05;输入"980505050505',插入数据库的DATETIME为1998-05-05 05:05:05。
(3)以 YYYYMMDDHHMMSS或者YYMMDDHHMMSS数字格式表示的日期和时间,例如输入 20121231050505,插入数据库的DATETIME为2012-12-31 05:05:05;输入981231050505,插入数据的DATETIME为1998-12-31 05:05:05。
【例13】创建数据表tmp6,定义数据类型为DATETIME的字段dt,向表中插入"YYYY-MM-DD HH:MM:SS"和"YYYYMMDDHHMMSS"字符串格式的日期和时间值,SQL语句如下:
首先创建表tmp6:
sql
CREATE TABLE tmp6 (dt DATETIME);
向表中插入"YYYY-MM-DD HH:MM:SS"和"YYYYMMDDHHMMSS"格式日期:
sql
INSERT INTO tmp6 values ('1998-08-08 08:08:08'),
('19980808080808'),('20101010101010');
查看插入结果:
sql
SELECT * FROM tmp6;
可以看到,各个不同类型的日期值都正确地插入到了数据表中。
【例14】向tmp6表中插入"YY-MM-DD HH:MM:SS"和"YYMMDDHHMMSS"字符串格式日期和时间值,SQL语句如下:
首先删除表中的数据:
sql
DELETE FROM tmp6;
向表中插入"YY-MM-DD HH:MM:SS"和"YYMMDDHHMMSS"格式日期:
sql
INSERT INTO tmp6 values ('99-09-09 09:09:09'),
('990909090909'),('101010101010');
查看插入结果;
sql
SELECT * FROM tmp6;
【例15】向tmp6表中插入YYYYMMDDHHMMSS 和YYMMDDHHMMSS 数字格式日期和时间值,SQL语句如下:
首先删除表中的数据;
sql
DELETE FROM tmp6;
向表中插入YYYYMMDDHHMMSS和 YYMMDDHHMMSS 数字格式日期和时间;
sql
INSERT INTO tmp6 values(19990909090909), (1010101010101);
查看插入结果:
sql
SELECT FROM tmp6;
【例16】向tmp6表中插入系统当前日期和时间值,SQL语句如下:
首先删除表中的数据:
sql
DELETE FROM tmp6;
向表中插入系统当前日期:
sql
INSERT INTO tmp6 values(NOW());
NOW()函数返回当前系统的日期和时间值,格式为"YYYY-MM-DD HH:MM:SS"。
提示:MySQL允许"不严格"语法:任何标点符号都可以用作日期部分或时间部分之间的间隔符。例如,'98-12-3111:30:45"、'98.12.31 11+30+45'、 '98/12/3111*30*45'和"98@12@311130^45'是等价的,这些值都可以正确地插入数据库。
5,TIMESTAMP
TIMESTAMP的显示格式与 DATETIME相同,显示宽度固定在19个字符,日期格式为YYYY-MM-DD HH:MM:SS,在存储时需要4个字节。但是 TIMESTAMP列的取值范围小于DATETIME的取值范围,为'1970-01-01 00-:00:01'UTC~"2038-01-1903:14:07'UTC,其中,UTC (Coordinated Universal Time),为世界标准时间,因此在插入数据时,要保证在合法的取值范围内。
【例17】创建数据表tmp7,定义数据类型为TIMESTAMP的字段ts,向表中插入值'19950101010101' , '950505050505', '1996-02-0202:02:02', '97@03@0303@03@03', 121212121212,NOW()
SQL语句如下:
sql
CREATE TABLE tmp7(ts TIMESTAMP);
向表中插入数据:
sql
INSERT INTO tmp7 values ('19950101010101'), ('950505050505'),
('1996-02-02 02:02:02'), ('97@03@03 03@03@03'), (121212121212), (
NOW());
查看插入结果:
sql
SELECT * FROM tmp7;
由结果可以看到,'19950101010101'被转换为1995-01-01 01:01:01;'950505050505'被转换为1995-05-05 05:05:05;'1996-02-0202:02:02'被转换为1996-02-02 02:02:02;'97@03@03 03@03@03'被转换为1997-03-03 03:03:03 121212121212被转换为2012-12-1212:12:12;NOWO被转换为系统当前日期时间2016-03-24 09:17:49。
提示:TIMESTAMP与 DATETIME除了存储字节和支持的范围不同外,还有一个最大的区别就是:DATETIME在存储日期数据时,按实际输入的格式存储,即输入什么就存储什么,与时区无关;而TIMESTAMP值的存储是以UTC(世界标准时间)格式保存的,存储时对当前时区进行转换,检索时再转换回当前时区。即查询时,根据当前时区的不同,显示的时间值是不同的。
【例18】向 tmp7表中插入当前日期,查看插入值,更改时区为东10区,再次查看插入值,SQL语句如下:
首先删除表中的数据:
sql
DELETE FROM tmp7;
向表中插入系统当前日期:
sql
INSERT INTO tmp7 values(NOW());
查看当前时区下日期值:
sql
SELECT* FROM tmp7;
查询结果为插入时的日期值,一般所在时区一般为东8区,下面修改当前时区为东10区,SQL语句如下:
sql
set time_zone='+10:00';
再次查看插入时的日期值:
sql
SELECT * FROM tmp7;
由结果可以看到,因为东10区时间比东8区快2个小时,因此查询的结果经过时区转换之后,显示的值增加了2个小时。相同的,如果时区每减小一个值,则查询显示的日期中的小时数减1。
提示:如果为一个DATETIME或TIMESTAMP对象分配一个DATE值,结果值的时间部分被设置为'00:00:00',因为DATE值未包含时间信息。如果为一个DATE对象分配一个DATETIME或TIMESTAMP值,结果值的时间部分被删除,因为DATE值未包含时间信息。
4,文本字符串类型
字符串类型用来存储字符串数据,除了可以存储字符串数据之外,还可以存储其他数据,比如图片和声音的二进制数据。MySQL支持两类字符型数据:文本字符串和二进制字符串。本节主要讲解文本字符串类型,文本字符串可以进行区分或者不区分大小写的串比较;另外,还可以进行模式匹配查找。MySQL 中文本字符串类型指CHAR、VARCHAR、TEXT、ENUM和 SET。表5列出了MySQL中的文本字符串数据类型。
表5 MySQL中文本字符串数据类型
|------------|-------------------------|-------------------------------------|
| 类型 | 说明 | 存储需求 |
| CHAR(M) | 固定长度非二进制字符串 | M字节,1<=M<= 255, |
| VARCHAR(M) | 变长非二进制字符串 | L+1字节,在此L<=M和1<= M<= 255 |
| TINYTEXT | 非常小的非二进制字符串 | L+1字节,在此L<2^8 |
| TEXT | 小的非二进制字符串 | L+2字节,在此L<2^16 |
| MEDIUMTEXT | 中等大小的非二进制字符串 | L+3字节,在此L<2^24 |
| LONGTEXT | 大的非二进制字符串 | L+4 字节,在此L<2^32 |
| ENUM | 枚举类型,只能有一个枚举字符串值 | 1或2个字节,取决于枚举值的数目(最大值65535) |
| SET | 一个设置,字符串对象可以有零个或多个SET成员 | 1, 2, 3, 4或8个字节,取决于集合成员的数量(最多64个成员) |
VARCHAR和TEXT类型与下一小节讲到的BLOB一样是变长类型,对于其存储需求取决于列值的实际长度(在前面的表格中用L表示),而不是取决于类型的最大可能尺寸。
例如,一个VARCHAR(10)列能保存最大长度为10个字符的一个字符串,实际的存储需要是字符串的长度L,加上1个字节以记录字符串的长度。对于字符"abcd",L是4而存储要求是5个字节。
1,CHAR和VARCHAR类型
CHAR(M)为固定长度字符串,在定义时指定字符串列长。当保存时在右侧填充空格以达到指定的长度。M表示列长度,M的范围是0~255个字符。例如,CHAR(4)定义了一个固定长度的字符串列,其包含的字符个数最大为4。当检索到CHAR值时,尾部的空格将被删除掉。
VARCHAR(M)是长度可变的字符串,M表示最大列长度。M 的范围是 0~-65535。VARCHAR的最大实际长度由最长的行的大小和使用的字符集确定,而其实际占用的空间为字符串的实际长度加1。例如,VARCHAR(50)定义了一个最大长度为50的字符串,如果插入的字符串只有10个字符,则实际存储的字符串为10个字符和一个字符串结束字符。VARCHAR在值保存和检索时尾部的空格仍保留。
【例19】下面将不同字符串保存到CHAR(4)和 VARCHAR(4)列,说明CHAR和VARCHAR之间的差别,如表6所示。
表6 CHAR(4)与 VARCHAR(4)存储区别
|----------|----------|------|---------|------|
| 插入值 | CHAR(4) | 存储需求 | CHAR(4) | 存储需求 |
| '' | ' ' | 4字节 | '' | 1字节 |
| 'ab' | 'ab ' | 4字节 | 'ab ' | 3字节 |
| 'abc' | 'abc' | 4字节 | 'abc' | 4字节 |
| 'abcd' | 'abcd' | 4字节 | 'abcd' | 5字节 |
| 'abcdef' | 'abcdef' | 4字节 | 'abcd' | 5字节 |
对比结果可以看到,CHAR(4)定义了固定长度为4的列,不管存入的数据长度为多少,所占用的空间均为4个字节。VARCHAR(4)定义的列所占的字节数为实际长度加1。
当查询时CHAR(4)和 VARCHAR(4)的值并不一定相同,如【例20】所示。
【例20】创建tmp8表,定义字段ch和 vch 数据类型依次为CHAR(4)、VARCHAR(4)向表中插入数据"ab ",SQL语句如下:
创建表tmp8:
sql
CREATE TABLE tmp8(
ch CHAR(4),
vch VARCHAR (4)
);
输入数据:
sql
INSERT INTO tmp8 VALUES('ab ', 'ab ');
查询结果:
sql
SELECT concat('(',ch, ')'),concat('(', vch, ')') FROM tmp8;
从查询结果可以看到,ch 在保存"ab "时将末尾的两个空格删除了,而 vch 字段保留了末尾的两个空格。
提示:在表 5.7 中,最后一行的值只有在使用"不严格"模式时,字符串才会被截断插入;如果MySQL 运行在"严格"模式,超过列长度的值不会被保存,并且会出现错误信息:"ERROR
1406(22001): Data too long for column",即字符串长度超过指定长度,无法插入。
2,TEXT类型
TEXT 列保存非二进制字符串,如文章内容、评论等。
当保存或查询 TEXT列的值时,不删除尾部空格。
Text类型分为4种:TINYTEXT、TEXT、MEDIUMTEXT 和 LONGTEXT
不同的TEXT类型的存储空间和数据长度不同。
(1)TINYTEXT 最大长度为 255(2^8-1)字符的TEXT列
(2)TEXT 最大长度为 65535(2^16-1)字符的TEXT 列。
(3)MEDIUMTEXT 最大长度为16777215(2^24-1)字符的TEXT 列
(4)LONGTEXT 最大长度为 4294967295或4GB(2^32-11)字符的TEXT 列。
3,ENUM 类型
ENUM 是一个字符串对象,其值为表创建时在列规定中枚举的一列值。语法格式如下:
sql
字段名 ENUM('值1','值2',....'值n'
字段名指将要定义的字段,值 n 指枚举列表中的第n 个值。ENUM 类型的字段在取值时
只能在指定的枚举列表中取,而且一次只能取一个。如果创建的成员中有空格时,其尾部的空
格将自动被删除。ENUM 值在内部用整数表示,每个枚举值均有一个索引值:列表值所允许
的成员值从1开始编号,MySOL存储的就是这个索引编号。枚举最多可以有 65 535个元素
例如定义ENUM类型的列(first,secondthird),该列可以取的值和每个值的索引如表7 所示
表7 ENUM类型的取值范围
|--------|------|
| 值 | 索引 |
| NULL | NULL |
| '' | 0 |
| first | 1 |
| second | 2 |
| third | 3 |
ENUM 值依照列索引顺序排列,并且空字符串排在非空字符串前,NULL 值排在其他所
有的枚举值前。这一点也可以从表7 中看到。
在这里,有一个方法可以查看列成员的索引值,如[例21]所示
【例21】创建表tmp9,定义ENUM 类型的列enm('first','second','third'),查看列成员
的索引值,SQL语句如下:
首先,创建tmp9表:
sql
CREATE TABLE tmp9(enm ENUM('first', 'second', 'third') );
插入各个列值:
sql
INSERT INTO tmp9 values ('first'), ('second'), ('third'), (NULL);
查看索引值:
sql
SELECT enm, enm+0 FROM tmp9;
可以看到,这里的索引值和前面所述的相同。
提示:ENUM 列总有一个默认值。如果将 ENUM 列声明为 NULL,NULL值则为该列的一个有效值,并且默认值为NULL。如果 ENUM 列被声明为 NOTNULL,其默认值为允许的值列表的第1个元素。
[例22]创建表 tmp10,定义INT 类型的 soc 字段,ENUM 类型的字段 level,列表值
为(excellent, good, bad),向表 tmp10 中插入数据'good',1,2,3,'best',SQL语句如下
首先,创建数据表:
sql
CREATE TABLE tmp10 (
soc INT,
level enum('excellent', 'good', 'bad')
);
插入数据:
sql
INSERT INTO tmp10 values(70, 'good'), (90,1), (75,2), (50,3);
再次插入数据:
sql
INSERT INTO tmp10 values (100, 'best');
ERROR 1265 (01000): Data truncated for column 'level' at rowl
这里系统提示错误信息,可以看到,由于字符串值"best"不在 ENUM 列表中,所以对数据进行了阻止插入操作,查询结果如下:
sql
SELECT * FROM tmp10;
由结果可以看到,因为 ENUM 列表中的值在 MySQL 中都是以编号序列存储的,因此,
插入列表中的值"good"或者插入其对应序号"2'的结果是相同的;"best"不是列表中的
值,因此不能插入数据。
4,SET类型
SET 是一个字符串对象,可以有零或多个值,SET 列最多可以有 64 个成员,其值为表创
建时规定的一列值。指定包括多个 SET 成员的SET 列值时,各成员之间用号)间隔开。语法格式如下:
sql
SET('值1','值2',...'值n;)
与ENUM类型相同,SET 值在内部用整数表示,列表中每一个值都有一个索引编号。当创建表时,SET 成员值的尾部空格将自动被删除。但与 ENUM 类型不同的是,ENUM 类型的字段只能从定义的列值中选择一个值插入,而 SET 类型的列可从定义的列值中选择多个字符的联合。
如果插入 SET 字段中列值有重复,则 MySQL自动除重复的值;插入 SET 字段的值的顺序并不重要,MySQL会在存入数据库时,按照定义的顺序显示;如果插入了不正确的值,默认情况下,MySQL将忽视这些值,并给出警告。
[例23]创建表 tmp11,定义 SET 类型的字段s,取值列表为('a','b','c','d'),插入数据('a')
('a,b,a'),(c,a,d),('a,x,b,y'),SQL 语句如下:
首先创建表tmp11
sql
CREATE TABLE tmp11 (s SET('a','b','c','d'));
插入数据:
sql
INSERT INTO tmp11 values('a'), ('a,b,a'), ('c,a,d');
再次插入数据:
sql
INSERT INTO tmp11 values ('a,x,b,y');
ERROR 1265 (01000): Data truncated for column 's' at row 1
由于插入了SET列不支持的值,因此MySQL给出错误提示
查看结果:
sql
SELECT * FROM tmp11;
从结果可以看到,对于 SET 来说如果插入的值为重复的,则只取一个,例如"a,b,a"
则结果为"a,b";如果插入了不按顺序排列值,则自动按顺序插入,例如"c,a,d",结果为
"a,c,d";如果插入了不正确值,该值将被阻止插入,例如插入值"a,x,b,y"
5,二进制字符串类型
前面讲解了存储文本的字符串类型,这一小节将讲解 MySOL中存储二进制数据的字符串类型。MySQL中的二进制数据类型有:BIT、BINARY、VARBINARY、TINYBLOB、BLOB
MEDIUMBLOB和LONGBLOB,本节将讲解各类二进制字符串类型的特点和使用方法。表8列出了MySQL中的二进制数据类型。
表8 MySQL中的二进制字符串类型
|---------------|------------|-------------------|
| 类型 | 说明 | 存储需求 |
| BIT(M) | 位字段类型 | 大约(M+7)/8个字节 |
| BINARY(M) | 固定长度二进制字符串 | M个字节 |
| VARBINARY(M) | 可变长度二进制字符串 | M+1个字节 |
| TINYBLOB(M) | 非常小的BLOB | L+1字节,在此L<2^8 |
| BLOB(M) | 小BLOB | L+2字节,在此L<2^16 |
| MEDIUMBLOB(M) | 中等大小的BLOB | L+3 字节,在此L<2^24 |
| LONGBLOB(M) | 非常大的BLOB | L+4 字节,在此L<2^32 |
1,BIT 类型
BIT 类型是位字段类型。M 表示每个值的位数,范围为 1~64。如果 M 被省略,默认为1。如果为 BIT(M)列分配的值的长度小于 M 位,在值的左边用 0 填充。例如,为 BIT(6)列分配一个值 b"101,其效果与分配 b000101'相同。BIT 数据类型用来保存位字段值,例如:以二进制的形式保存数据 13,13 的二进制形式为 1101,在这里需要位数至少为 4位的 BIT 类型,即可以定义列类型为 BIT(4)。大于二进制 1111 的数据是不能插入 BIT(4)类型的字段中的
[例24]创建表 tmp12,定义 BIT(4)类型的字段 b,向表中插入数据2、915。
首先创建表tmp12,SQL语句如下:
sql
CREATE TABLE tmp12(b BIT(4));
插入数据:
sql
INSERT INTO tmp12 VALUES(2), (9), (15);
查询插入结果:
sql
SELECT BIN(b+0) FROM tmp12;
b+0 表示将二进制的结果转换为对应的数字的值,BIN(函数将数字转换为二进制。从结果可以看到,成功地将 3 个数插入到表中。
提示:默认情况下,MySQL 不可以插入超出该列允许范围的值,因而插入的数据要确保插入的值在指定的范围内。
2,BINARY和VARBINARY 类型
1BINARY和VARBINARY类型类似于CHAR和VARCHAR,不同的是它们包含二进制字节字符串。其使用的语法格式如下:
sql
列名称 BINARY(M) 或者 VARBINARY(M)
BINARY 类型的长度是固定的 指定长度之后不足最大长度的 将在它们右边填充"0"补齐以达到指定长度。例如:指定列数据类型为 BINARY(3),当插入"a'时,存储的内容实际为"a\00",当插入"ab"时,实际存储的内容为"abl0",不管存储的内容是否达到指定的长度,其存储空间均为指定的值 M。
VARBINARY 类型的长度是可变的,指定好长度之后,其长度可以在 0 到最大值之间。
例如:指定列数据类型为 VARBINARY(20),如果插入的值的长度只有 10,则实际存储空间
为 10加1,即其实际占用的空间为字符串的实际长度加 1。
[例25]创建表tmp13,定义BINARY(3)类型的字段b和VARBINARY(3)类型的字段并向表中插入数据"5',比较两个字段的存储空间。
首先创建表tmp13,输入SQL语句如下:
sql
CREATE TABLE tmp13(
b BINARY(3),
vb VARBINARY(30)
);
插入数据:
sql
INSERT INTO tmp13 VALUES(5, 5);
查看两个字段存储数据的长度
sql
SELECT length(b), length(vb) FROM tmp13;
可以看到 字段的值数据长度为 3而 字段的数据长度仅为插入的一个字符的长度1。如果想要进一步确认"5'在两个字段中不同的存储方式,输入如下语句:
sql
SELECT b, vb, b='5', b='5\0\0', vb='5', vb= '15\0\0' FROM tmp13;
由执行结果可以看出,b 字段和 vb 字段的长度是截然不同的,因为 b 字段不足的空间填
充了"\0',而 vb 字段则没有填充
3,BLOB类型
BLOB是一个二进制大对象用来存储可变数量的数据。BLOB类型分为4种:TINYBLOB
BLOB、MEDIUMBLOB 和LONGBLOB,它们可容纳值的最大长度不同,如表9 所示。
表9 BLOB类型的存储范围
|------------|---------------------------------|
| 数据类型 | 存储需求 |
| TINYBLOB | 最大长度为255(2^8-1)B |
| BLOB | 最大长度为65535(2^16-1)B |
| MEDIUMBLOB | 最大长度为16777215(2^24-1)B |
| LONGBLOB | 最大长度为4294967295B或4GB(22^32-1)B |
BLOB 列存储的是二进制字符串 (字节字符串);TEXT 列存储的是非二进制字符串(字符字符串)。BLOB 列没有字符集,并且排序和比较基于列值字节的数值:TEXT 列有一个字符集,并且根据字符集对值进行排序和比较。
2,如何选择数据类型
MySQL 提供了大量的数据类型,为了优化存储,提高数据库性能,在任何情况下均应使
用最精确的类型。即在所有可以表示该列值的类型中,该类型使用的存储最少。
1,整数和浮点数
如果不需要小数部分,则使用整数来保存数据:如果需要表示小数部分,则使用浮点数类
型。对于浮点数据列,存入的数值会对该列定义的小数位进行四舍五入。例如,如果列的值的
范围为1~99999,若使用整数,则 MEDIUMINT UNSIGNED 是最好的类型若需要存储小数
则使用 FLOAT类型。
浮点类型包括 FLOAT和DOUBLE 类型。DOUBLE 类型精度比FLOAT类型高,因此如要求存储精度较高时,应选择 DOUBLE类型。
2,浮点数和定点数
浮点数 FLOAT、DOUBLE 相对于定点数 DECIMAL的优势是:在长度一定的情况下,浮点数能表示更大的数据范围,但是由于浮点数容易产生误差,因此对精确度要求比较高时,建议使用DECIMAL来存储。DECIMAL在MySOL中是以字符串存储的,用于定义货币等对精确度要求较高的数据。在数据迁移中,float(M,D)是非标准 SQL定义,数据库迁移可能会出现问题,最好不要这样使用。另外两个浮点数进行减法和比较运算时也容易出问题,因此在进行计算的时候,一定要小心。如果进行数值比较,最好使用 DECIMAL类型。
3,日期与时间类型
MySOL对于不同种类的日期和时间有很多的数据类型,比如 YEAR 和TIME。如果只需
要记录年份,则使用 YEAR 类型即可;如果只记录时间,只需使用 TIME类型。
如果同时需要记录日期和时间,则可以使用 TIMESTAMP 或者 DATETIME类型。由于TIMESTAMP 列的取值范围小于 DATETIME 的取值范围,因此存储范围较大的日期最好使用
DATETIME。
TIMESTAMP也有一个DATETIME不具备的属性。默认的情况下,当插入一条记录但并
没有指定TIMESTAMP 这个列值时,MySQL 会把 TIMESTAMP 列设为当前的时间。因此当
需要插入记录同时插入当前时间时,使用 TIMESTAMP 是方便的,另外TIMESTAMP 在空间
上比DATETIME 更有效。
4,CHAR与VARCHAR之间的特点与选择
CHAR 和VARCHAR 的区别如下:
CHAR 是固定长度字符,VARCHAR 是可变长度字符:
CHAR 会自动删除插入数据的尾部空格,VARCHAR 不会删除尾部空格
CHAR 是固定长度,所以它的处理速度比 VARCHAR 的速度要快,但是它的缺点就是浪费存储空间。所以对存储不大,但在速度上有要求的可以使用 CHAR 类型,反之可以使用VARCHAR类型来实现。
存储引擎对于选择CHAR和VARCHAR的影响:
对于MyISAM存储引警: 最好使用固定长度的数据列代替可变长度的数据列。这样可以使整个表静态化,从而使数据检索更快,用空间换时间。
对于InnoDB存储引擎: 使用可变长度的数据列,因为InnoDB 数据表的存储格式不分固定长度和可变长度,因此使用 CHAR 不一定比使用 VARCHAR 更好,但由于VARCHAR 是按照实际的长度存储,比较节省空间,所以对磁盘 I/O 和数据存储总量比较好。
5,ENUM和SET
ENUM 只能取单值,它的数据列表是一个枚举集合。它的合法取值列表最多允许有65535
个成员。因此,在需要从多个值中选取一个时,可以使用 ENUM。比如:性别字段适合定义
为 ENUM类型,每次只能从"男'或"女'中取一个值。
SET 可取多值。它的合法取值列表最多允许有 64 个成员。空字符串也是一个合法的 SET
值。在需要取多个值的时候,适合使用 SET 类型,比如:要存储一个人兴趣爱好,最好使用
SET 类型。
ENUM 和SET的值是以字符串形式出现的,但在内部,MySQL 以数值的形式存储它们。
6,BLOB和TEXT
BLOB 是二进制字符串,TEXT 是非二进制字符串,两者均可存放大容量的信息。BLOB主要存储图片、音频信息等,而 TEXT 只能存储纯文本文件。
3,数据类型优化
设计字段有一个基本的原则,保小不保大,也就是能够用字节少的字段就不用字节数大的字段,目的是为了节省空间,提高查询效率。
更小的字段,占用更小的磁盘空间,内存空间,更小的 IO 消耗。
1,数值类型
**手机号:**通常我们在存储手机号的时候,喜欢用 Varchar 类型。
如果是 11 位的手机号,假设我们用 utf8 的编码,每位字节就需要 3 个字节,那么就需要 11*33=33 个字节来存放;如果我们使用 bigint,只需要 8 个字节就可以存放。
**IP 地址:**同上,IP 地址也可以通过 int(4 字节)在存放,可以通过 INET_ATON() 函数把 IP 地址转成数字。这里需要注意溢出的问题,需要用无符号的 int。
**年龄,枚举类型:**可以用 tinyint 来存放,它只占用 1 个字节,无符号的 tinyint 可以表示 0-255 的范围,基本够用了。
2,字符类型
Char 和 Varchar 是我们常用的字符类型。char(N) 用来记录固定长度的字符,如果长度不足 N 的,用空格补齐。
varchar(N) 用来保存可变长度的字符,它会额外增加 1-2 字节来保存字符串的长度。
Char 和 Varchar 占用的字节数,根据数据库的编码格式不同而不同。Latin1 占用 1 个字节,gbk 占用 2 个字节,utf8 占用 3 个字节。
用法方面,如果存储的内容是可变长度的,例如:家庭住址,用户描述就可以用 Varchar。
如果内容是固定长度的,例如:UUID(36 位),或者是 MD5 加密串(32 位),就可以使用 Char 存放。
3,时间类型
Datetime 和 Timestamp 都是可以精确到秒的时间类型,但是 Datetime 占用 8 个字节,而 Timestamp 占用 4 个字节。
所以在日常建表的时候可以有限选择 Timestamp。不过他们有下面几个小区别,需要注意的。
**区别一:**存储数据方式不一样。Timestamp 是转化成 utc 时间进行存储,查询时,转化为客户端时间返回的。
**区别二:**两者存储时间的范围不一样。
Timestamp 为'1970-01-01 00:00:01.000000' 到'2038-01-19 03:14:07.999999'。
Datetime为'1000-01-01 00:00:00.000000'到'9999-12-31 23:59:59.999999'。
总结:
选择合适的数据类型是很重要的。如要求存储精度较高时,应选择 DOUBLE类型。如果进行数值比较,最好使用 DECIMAL类型。 如果字符串是固定长度的,使用char,日期类型,就不适合字符串存储。TEXT 存储纯文本文件。BLOB主要存储图片、音频信息等。
上一篇: 《mysql 常见数据表操作》
下一篇: 《mysql 运算符》