前言
✨欢迎来到小K的mysql专栏,本节将为大家带来MySQL数据类型简介 | 整数 | 浮点 | 定点 | 时间/日期类型的分享✨
0.数据类型简介
数据类型(data_type)是指系统中所允许的数据的类型。MySQL 数据类型定义了列中可以存储什么数据以及该数据怎样存储的规则。
数据库中的每个列都应该有适当的数据类型,用于限制或允许该列中存储的数据。例如,列中存储的为数字,则相应的数据类型应该为数值类型。
如果使用错误的数据类型可能会严重影响应用程序的功能和性能,所以在设计表时,应该特别重视数据列所用的数据类型。更改包含数据的列不是一件小事,这样做可能会导致数据丢失。因此,在创建表时必须为每个列设置正确的数据类型和长度。
MySQL 的数据类型有大概可以分为 5 种,分别是整数类型、浮点数类型和定点数类型、日期和时间类型、字符串类型、二进制类型等。
注意:整数类型和浮点数类型可以统称为数值数据类型。
1 整数类型
1.1 类型介绍
整数类型包括 TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT
类型 | 说明 | 大小(字节) |
---|---|---|
TINYINT | 很小的整数 | 1 |
SMALLINT | 小的整数 | 2 |
MEDIUMINT | 中等大小的整数 | 3 |
INT/INTEGER | 普通大小的整数 | 4 |
BIGINT | 大整数 | 8 |
1.2 可选属性
1.2.1 M
M:表示显示宽度,M的取值范围是(0,255)。例如,INT(5):当数据小于5位时,可以用字符填充 补满所需宽度。该功能需要配置ZEROFILL
使用,表示用 0 填满宽度,否则指定显示宽度无效。
注意:设置宽度不会对原来的数据或者插入数据产生任何影响,仅仅是显示效果不同。从MySQL 8.0.17开始,整数类型不推荐使用显示宽度属性。
1.2.2 UNSIGNED
所有整数类型都有一个可选的属性UNSIGNED,无符号整数类型的最小取值为0。所以,如果需要在MySQL中保存非负整数是,可以将整数类型设置为无符号类型。
1.3 使用场景
TINYINT:一般用于枚举类型,比如系统设定取值范围很小且固定的场景。
SMALLINT:可以用于较小范围的统计数据,比如统计工厂的固定资产库存数量等。
MEDIUMINT:用于较大整数的计算,比如车站每日的客流量等。
INT、INTEGER:取值范围足够大,一般情况下不用考虑超限问题,用的最多,比如商品编号。
BIGINT:只有当你处理特别巨大的整数时才会用到,比如双十一的交易量、大型门户网站点击量、证券公司衍生产品持仓等。
1.4 如何选择
在评估用哪种整数类型的时候,你需要考虑存储空间
和可靠性
的平衡问题:一方面,用占用字节数少的整数类型可以节省存储空间;另一方面,要是为了节省存储空间,使用的整数类型取值范围太小,一旦遇到超出取值范围的情况,就可能引起系统错误
,影响可靠性。
举个栗子,商品编号采用的数据类型是INT。原因就在于,客户门店中流通的商品种类较多,而且,每天都有旧的商品下架,新的商品上架,这样不断迭代,日积月累。
如果使用 SMALLINT 类型,虽然占用字节数比 INT 类型的证书少,但是却不能保证数据不会超出 655345。相反,使用INT 就能确保有足够大的取值范围,不用担心数据超出范围影响可靠性。
你要注意的是,在实际工作中,系统故障产生的成本远远超过增加几个字段存储空间所产生的成本
。因此,我建议你首先确保数据不会超过取值范围,在这个前提下,再去考虑如何节省存储空间。
2 浮点类型
2.1 类型介绍
浮点数类型包括 FLOAT、DOUBLE和REAL。
类型 | 说明 | 大小(字节) |
---|---|---|
FLOAT | 单精度浮点数 | 4 |
DOUBLE | 双精度浮点数 | 8 |
REAL | 默认为DOUBLE | 把SQL模式设定为“REAL_AS_FLOAT ”,REAL就是FLOATSET sql_mode = "REAL_AS_FLOAT"
|
2.2 精度说明
-
MySQL允许使用
非标准语法
(其他数据库未必支持,因此如果涉及到数据前移,则最好不要这么用):FLOAT(M,D)或DOUBLE(M,D)。其中M
称为精度,表示总共的位数;D
称为标度,表示小数的位数。D<=M<=255,0<=D<=30。例如:定义为FLOAT(5,2)的一个列可以显示为[-999.99,999.99],超出这个范围会报错
-
FLOAT 和 DOUBLE 在不指定精度时,默认会按照实际的精度(由计算机硬件和操作系统决定)。
-
注意:浮点类型也可以加
UNSIGNED
,但是不会改变数据范围,例如:FLOAT(3,2) UNSIGNED任然只能表示0-9.99的范围。 -
不管是否显示设置了精度(M,D),MySQL有自己的处理方案:
- 如果存储时,整数部分超出了范围,则直接报错。
- 如果存储时,小数点部分超出范围,就分一下情况:
- 若四舍五入后,整数部分没有超出范围,就可以存储。如在FLOAT(5,2)列插入999.009,近似结果是999.01。
- 若四舍五入后,整数部分超出范围,则直接报错。如在FLOAT(5,2)列插入999.995和-999.995都会报错。
-
从MySQL8.0.17开始,FLOAT(M,D)和DOUBLE(M,D)用法在官方文档中已经明确不推荐使用了,将来可能会被移除;另外关于浮点类型FLOAT和DOUBLE的UNSIGNED也不推荐使用了,将来也可能被移除。
浮点数相对于定点数的优点是在长度一定的情况下,浮点数能够表示更大的范围;缺点是会引起精度问题。
最后再强调一下:在 MySQL 中,定点数以字符串形式存储,在对精度要求比较高的时候(如货币、科学数据),使用 DECIMAL 的类型比较好,另外两个浮点数进行减法和比较运算时也容易出问题,所以在使用浮点数时需要注意,并尽量避免做浮点数比较
3 定点类型
定点数类型为 DECIMAL。
类型 | 说明 | 大小(字节) |
---|---|---|
DECIMAL/DEC | 压缩的“严格”定点数 | M+2 |
DECIMAL(M,D)。其中M
称为精度,表示总共的位数;D
称为标度,表示小数的位数。0<=M<=65,0<=D<=30,D<M。例如:定义为DECIMAL(5,2),表示该列的取值范围是[-999.99,999.99]。
- DECIMAL(M,D)的最大值范围与DOUELE类型一样,但是有效的数据方位是由M和D决定的。DECIMAL的存储空间并不固定,由精度M决定,总共占用的存储空间为M+2个字节。也就是说,在一些对精度要求不高的场景下,比起占用同样字节长度的定点数,浮点数表达的数值范围可以更大。
- 定点数在MySQL内部是以字符串的形式进行存储的,这就决定了它一定是精确的。
- 当DECIMAL类型不指定精度和标度时,其默认为DECIMAL(10,0),当数据的精度超出了定点数的精度范围时,则MySQL同样会进行四舍五入。
-
浮点数 vs 定点数
- 浮点数相对于定点数的有点是在长度一定的情况下,浮点类型取值范围大,但是不精确,适用于需要取值范围大,又可以容忍误差的科学计算场景
- 定点数类型取值范围相对小,但是精准,没有误差,设置对于精度要求极高的场景(比如设计金额计算的场景)
4 日期/时间类型
包括 YEAR、TIME、DATE、DATETIME 和 TIMESTAMP。
类型 | 说明 | 日期范围 | 大小(字节) |
---|---|---|---|
YEAR | YYYY | 1901~2155 | 1 |
TIME | HH:MM:SS | -838:59:59 ~ 838:59:59 | 3 |
DATE | YYYY-MM-DD | 1000-01-01 ~ 9999-12-3 | 3 |
DATETIME | YYYY-MM-DD HH:MM:SS | 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59 | 8 |
TIMESTAMP | YYYY-MM-DD HH:MM:SS | 1970-01-01 00:00:00 UTC ~ 2038-01-19 03:14:07 UTC | 4 |
YEAR类型
YEAR 类型是一个单字节类型,用于表示年,在存储时只需要 1 个字节。可以使用各种格式指定 YEAR,如下所示:
- 以 4 位字符串或者 4 位数字格式表示 YEAR,范围为 ‘1901’~’2155’。输入格式为 ‘YYYY’ 或者 YYYY,例如,输入 ‘2010’ 或 2010,插入数据库的值均为 2010。
- 以 2 位字符串格式表示的 YEAR,范围为 ‘00’ 到 ‘99’。‘00’~’69’ 和 ‘70’~’99’ 范围的值分别被转换为 2000~2069 和 1970~1999 范围的 YEAR 值。‘0’ 与 ‘00’ 的作用相同。插入超过取值范围的值将被转换为 2000。
从MySQL5.5.27开始,2位格式的YEAR已经不推荐使用。YEAR默认格式就是“YYYY”,没必要写成YEAR(4)。
CREATE TABLE test_year
(
f1 YEAR,
f2 YEAR(4)
);
INSERT INTO test_year(f1) VALUES('2021'),(2022);
INSERT INTO test_year(f1) VALUES('2155');
-- out of range value for column 'f1' at row 1
INSERT INTO test_year(f1) VALUES('2156');
INSERT INTO test_year(f1) VALUES('69'),('70'); #2069 1970
INSERT INTO test_year(f1) VALUES(0),('0');
DATE 类型
DATE 类型用于仅需要日期值时,没有时间部分,在存储时需要 3 个字节。日期格式为 ‘YYYY-MM-DD’,其中 YYYY 表示年,MM 表示月,DD 表示日。
在给 DATE 类型的字段赋值时,可以使用字符串类型或者数字类型的数据插入,只要符合 DATE 的日期格式即可。如下所示:
- 以 ‘YYYY-MM-DD’ 或者 ‘YYYYMMDD’ 字符中格式表示的日期,取值范围为 ‘1000-01-01’~’9999-12-3’。例如,输入 ‘2015-12-31’ 或者 ‘20151231’,插入数据库的日期为2015-12-31。
- 以 ‘YY-MM-DD’ 或者 ‘YYMMDD’ 字符串格式表示日期,在这里YY表示两位的年值。MySQL 解释两位年值的规则:‘00~69’ 范围的年值转换为 ‘2000~ 2069’,‘70~99’ 范围的年值转换为 ‘1970~1999’。例如,输入 ‘15-12-31’,插入数据库的日期为 2015-12-31;输入 ‘991231’,插入数据库的日期为 1999-12-31。
- 使用 CURRENT_DATE() 或者 NOW(),插入当前系统日期。
提示:MySQL 允许“不严格”语法:任何标点符号都可以用作日期部分之间的间隔符。例如,‘98-11-31’、‘98.11.31’、‘98/11/31’和’98@11@31’ 是等价的,这些值也可以正确地插入数据库。
CREATE TABLE test_date1
(
f1 DATE
);
INSERT INTO test_date1 VALUES('2022-04-16'),('20220416'),(20220416);
INSERT INTO test_date1 VALUES('22-04-16'),('220416'),
('69-04-16'),('690416'),
('70-04-16'),('700416'),
('99-04-16'),('990416');
INSERT INTO test_date1 VALUES(220416),(690416),(700416),(990416);
INSERT INTO test_date1 VALUES(CURRENT_DATE()),(NOW());
TIME 类型
TIME 类型用于只需要时间信息的值,在存储时需要 3 个字节。格式为 HH:MM:SS。HH 表示小时,MM 表示分钟,SS 表示秒。
TIME 类型的取值范围为 -838:59:59~838:59:59,小时部分如此大的原因是 TIME 类型不仅可以用于表示一天的时间(必须小于 24 小时),还可能是某个事件过去的时间或两个事件之间的时间间隔(可大于 24 小时,或者甚至为负)。
可以使用各种格式指定 TIME 值,如下所示。
- ‘D HH:MM:SS’ 格式的字符串。还可以使用这些“非严格”的语法:‘HH:MM:SS’、‘HH:MM’、‘D HH’ 或 ‘SS’。这里的 D 表示日,可以取 0~34 之间的值。在插入数据库时,D 被转换为小时保存,格式为 “D*24+HH”。
- ‘HHMMSS’ 格式、没有间隔符的字符串或者 HHMMSS 格式的数值,假定是有意义的时间。例如,‘101112’ 被理解为’10:11:12’,但是 ‘106112’ 是不合法的(它有一个没有意义的分钟部分),在存储时将变为 00:00:00。
- 使用 CURRENT_TIME() 或者 NOW(),插入当前系统时间。
提示:为 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。
CREATE TABLE test_time1
(
f1 TIME
);
INSERT INTO test_time1
VALUES('10:35:30'),('2 10:35:30'),('10:35'),('2 10:35'),('1 35'),('30');
INSERT INTO test_time1 VALUES('103530'),(103530),(1035);
INSERT INTO test_time1 VALUES(NOW()),(CURRENT_TIME());
DATETIME 类型
DATETIME 类型用于需要同时包含日期和时间信息的值,在存储时需要 8 个字节。日期格式为 ‘YYYY-MM-DD HH:MM:SS’,其中 YYYY 表示年,MM 表示月,DD 表示日,HH 表示小时,MM 表示分钟,SS 表示秒。
在给 DATETIME 类型的字段赋值时,可以使用字符串类型或者数字类型的数据插入,只要符合 DATETIME 的日期格式即可,如下所示。
- 以 ‘YYYY-MM-DD HH:MM:SS’ 或者 ‘YYYYMMDDHHMMSS’ 字符串格式表示的日期,取值范围为 ‘1000-01-01 00:00:00’~’9999-12-3 23:59:59’。
- 以 ‘YY-MM-DD HH:MM:SS’ 或者 ‘YYMMDDHHMMSS’ 字符串格式表示的日期,在这里 YY 表示两位的年值。与前面相同,‘00~79’ 范围的年值转换为 ‘2000~2079’,‘80~99’ 范围的年值转换为 ‘1980~1999’。
- 以 YYYYMMDDHHMMSS 或者 YYMMDDHHMMSS 数字格式表示的日期和时间。
提示:**MySQL 允许“不严格”语法:任何标点符号都可用作日期部分或时间部分之间的间隔符。**例如,‘98-12-31 11:30:45’、‘98.12.31 11+30+35’、‘98/12/31 113045’ 和 ‘98@12@31 11 ^ 30 ^ 45’ 是等价的,这些值都可以正确地插入数据库。
CREATE TABLE test_datetime1
(
dt DATETIME
);
INSERT INTO test_datetime1 VALUES('2022-04-16 18:50:30'),('20220416185030');
INSERT INTO test_datetime1 VALUES('99-04-16 18:50:30'),('990416185030'),
('22-04-16 18:50:30'),('220416185030');
INSERT INTO test_datetime1 VALUES(20220202000000);
INSERT INTO test_datetime1 VALUES(CURRENT_TIMESTAMP()),(NOW());
TIMESTAMP 类型
TIMESTAMP 的显示格式与 DATETIME 相同,显示宽度固定在 19 个字符,日期格式为 YYYY-MM-DD HH:MM:SS,在存储时需要 4 个字节。但是 TIMESTAMP 列的取值范围小于 DATETIME 的取值范围,为 '1970-01-01 00:00:01’UTC~’2038-01-19 03:14:07’UTC。在插入数据时,要保证在合法的取值范围内。
提示:协调世界时(英:Coordinated Universal Time,法:Temps Universel Coordonné)又称为世界统一时间、世界标准时间、国际协调时间。英文(CUT)和法文(TUC)的缩写不同,作为妥协,简称 UTC。
TIMESTAMP 与 DATETIME 除了存储字节和支持的范围不同外,还有一个最大的区别是:
- DATETIME 在存储日期数据时,按实际输入的格式存储,即输入什么就存储什么,与时区无关;
- 而 TIMESTAMP 值的存储是以 UTC(世界标准时间)格式保存的,存储时对当前时区进行转换,检索时再转换回当前时区。即查询时,根据当前时区的不同,显示的时间值是不同的。
提示:如果为一个 DATETIME 或 TIMESTAMP 对象分配一个 DATE 值,结果值的时间部分被设置为 ‘00:00:00’,因此 DATE 值未包含时间信息。如果为一个 DATE 对象分配一个 DATETIME 或 TIMESTAMP 值,结果值的时间部分被删除,因此DATE 值未包含时间信息。
CREATE TABLE test_timestamp
(
ts TIMESTAMP
);
INSERT INTO test_timestamp VALUES('1999-01-01 01:02:03'),('1999-01-01 01:02:03'),
('99-01-01 01:02:03'),('990101010203');
INSERT INTO test_timestamp VALUES('2022@01@02@11@22@33');
INSERT INTO test_timestamp VALUES(CURRENT_TIMESTAMP()),(NOW());
TIMESTAMP 和 DATETIME区别
CREATE TABLE temp_time
(
d1 DATETIME,
d2 TIMESTAMP
);
INSERT INTO temp_time VALUES('2022-04-16 19:09:55','2022-04-16 19:09:55');
INSERT INTO temp_time VALUES(NOW(),NOW());
SELECT * FROM temp_time;
SET time_zone = '+9:00'
SELECT * FROM temp_time; #设置之后再查询一次
- 设置时区
SET time_zone = '+9:00'
TIMESTAMP妙用
TIMESTAMP时间戳在创建的时候可以有多重不同的特性,如:
1.在创建新记录和修改现有记录的时候都对这个数据列刷新:
TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
2.在创建新记录的时候把这个字段设置为当前时间,但以后修改时,不再刷新它:
TIMESTAMP DEFAULT CURRENT_TIMESTAMP
3.在创建新记录的时候把这个字段设置为0,以后修改时刷新它:
TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
4.在创建新记录的时候把这个字段设置为给定值,以后修改时刷新它:
TIMESTAMP DEFAULT ‘yyyy-mm-dd hh:mm:ss' ON UPDATE CURRENT_TIMESTAMP
模拟用户登录
CREATE TABLE t_user
(
id INT PRIMARY KEY AUTO_INCREMENT,
createtime TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
lastlogintime TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
is_online TINYINT DEFAULT 0
);
INSERT INTO t_user(is_online) VALUES(1);
SELECT * FROM t_user;
# 数据更新
UPDATE t_user SET is_online=0 WHERE id=1;
本来两个时间是一模一样的,但是当用户下线,lastlohintime
发生自动更新
总结
总的来说,选择合适的数据类型可以提高数据库的性能、节省存储空间、确保数据的一致性和有效性,并方便进行数据处理和计算。因此,在设计数据库时,选择合适的数据类型非常重要。~下节带来字符串 | 二进制类型的分享