【注】本章代码测试建议在 MySQL5.7中进行。
类型 | 类型举例 |
---|---|
整数类型 | TINYINT, SMALLINT, MEDIUMINT, INT(或INTEGER), BIGINT |
浮点类型 | FLOAT, DOUBLE |
定点数类型 | DECIMAL |
位类型 | BIT |
日期时间类型 | YEAR, TIME, DATE, DATETIME, TIMESTAMP |
文本字符串类型 | CHAR, VARCHAR, TINYTEXT, TEXT, MEDIUMTEXT, LONGTEXT |
枚举类型 | ENUM |
集合类型 | SET |
二进制字符串类型 | BINARY, VARBINARY, TINYBLOB, BLOB, MEDIUMBLOB, LONGBLOB |
JSON类型 | JSON对象,JSON数组 |
空间数据类型 | 单值:GEOMETRY, POINT, LINESTRING, POLYGON; 集合:MULTIPOINT, MULTILINESTRING, MULTIPOLYGON, GEOMETRYCOLLECTION |
常见数据类型的属性如下:
MySQL关键字 | 含义 |
---|---|
NULL | 数据列可包含NULL值 |
NOT NULL | 数据列不允许包含NULL值 |
DEFAULT | 默认值 |
PRIMARY KEY | 主键 |
AUTO_INCREMENT | 自动递增,适用于整数类型 |
UNSIGNED | 无符号 |
CHARACTER SET name | 指定一个字符集 |
整数类型 | 字节 | 有符号数范围 | 无符号数范围 |
---|---|---|---|
TINYINT | 1 | -128~127 | 0~255 |
SMALLINT | 2 | -32768~32767 | 0~65535 |
MEDIUMINT | 3 | -8388608~8388607 | 0~16777215 |
INT、INTEGER | 4 | -2147483648~2147483647 | 0~4294967295 |
BIGINT | 8 | -9223372036854775808~9223372036854775807 | 0~18446744073709551616 |
举个例子:
创建一张表,5 个字段分别是五种不同的整数类型。
CREATE TABLE test_int1(
f1 TINYINT,
f2 SMALLINT,
f3 MEDIUMINT,
f4 INT,
f5 BIGINT
);
查询结果:
如果我往字段 f1
中插入数据 128
,这就超过了 TINYINT 有符号的最大表示范围 127 。MySQL就会报错:
INSERT INTO test_int1(f1)
VALUES(128);
输出:
错误代码: 1264
Out of range value for column 'f1' at row 1
在5.7中查看字段属性,
DESC test_int1;
输出:
可以看到在 type
这一栏比 8.0 的数据类型后面多了一个括号。里面的数字表示的是每一个整数数据类型的数据宽度,如,TINYINT 的范围是 -128~127 ,带上负号一共占用 4 位宽度,因此 TINYINT 小括号里的数字就是 4
,其他以此类推。
由此衍生出一个属性,那就是 “显示宽度” 。
在创建表声明字段的数据类型时,可以在数据类型后面添加小括号来声明该数据类型的显示宽度,一般要搭配关键字 ZEROFILL 来使用。例如:
CREATE TABLE test_int2(
f1 INT,
f2 INT(5),
f3 INT(5) ZEROFILL
);
查询结果:
往该表中插入数据,并查询:
INSERT INTO test_int2(f1, f2, f3)
VALUES
(123, 123, 123),
(123456, 123456, 123456);
SELECT *
FROM test_int2;
查询结果:
【说明】
① 显示宽度并不会限制数据类型原来的表示范围。具体来说,当插入的数据大于显示宽度且在表示范围内时,数据正常显示;当插入的数据小于显示宽度且该字段被关键字 ZEROFILL 修饰时 (如 f3
),其不满足显示宽度的部分就会被 0 填充。如果插入数据小于显示宽度,但该字段没有被关键字 ZEROFILL 修饰时 (如字段 f2
) ,则不会被 0 填充,所以声明的显示宽度也没有意义。故字段的显示宽度一般需要与关键字 ZEROFILL 搭配使用。
② 从MySQL8.0.17开始,整数数据类型就不推荐使用显示宽度属性了。
③ 添加了关键字 ZEROFILL 的字段,MySQL默认就将其声明为无符号 unsigned
类型的字段。
在创建表格时可以使用关键字 UNSIGNED 来修饰该字段的数据类型,表示无符号,即该字段全是非负数的。
举个例子,
CREATE TABLE test_int3(
f1 INT UNSIGNED
);
在评估用哪种整数类型的时候,你需要考虑存储空间和可靠性的平衡问题:一方面,用占用字节数少的整数类型可以节省存储空间;另一方面,要是为了节省存储空间,使用的整数类型取值范围太小,一旦遇到超出取值范围的情况,就可能引起系统错误,影响可靠性。
举个例子,商品编号采用的数据类型是 INT 。原因就在于,客户门店中流通的商品种类较多,而且,每
天都有旧商品下架,新商品上架,这样不断迭代,日积月累。
如果使用 SMALLINT 类型,虽然占用字节数比 INT 类型的整数少,但是却不能保证数据不会超出范围 65535。相反,使用 INT,就能确保有足够大的取值范围,不用担心数据超出范围影响可靠性的问题。
你要注意的是,在实际工作中,系统故障产生的成本远远超过增加几个字段存储空间所产生的成本。因
此,建议首先确保数据不会超过取值范围,在这个前提之下,再去考虑如何节省存储空间。
MySQL表示小数的数据类型有两种,分别是浮点类型和定点类型。由于浮点类型精度不够高,容易造成精度丢失,因此在实际开发场景中常使用定点数类型来确保精度。
整数可以通过隐式转换赋给浮点数。
MySQL中浮点类型有三种:FLOAT、DOUBLE 和 REAL 。其中 REAL 其实就是 FLOAT 。
浮点类型 | 字节 |
---|---|
FLOAT | 4 |
DOUBLE | 8 |
【注意】
MySQL中的浮点数支持非标准语法 (即其他数据库未必支持,因此如果涉及到数据迁移,则最好不要这么使用) 。
FLOAT(M, D)
# 或
DOUBLE(M, D)
其中,M
称为精度,表示整数位+小数位;D
称为标度,表示小数位。
举个例子:
FLOAT(5, 2)
表示整数位有 3 位,小数位有 2 位,加起来一共 5 位,表示范围是 -999.99 ~ 999.99 。
【说明】
浮点数是不准确的,所以我们要避免使用 =
来判断两个浮点数是否相等。
同时,在一些对精确度要求较高的项目中,千万不要使用浮点数,不然会导致结果错误,甚至是造成不可挽回的损失。那么,MySQL 有没有精准的数据类型呢?当然有,这就是定点数类型: DECIMAL 。
定点类型 | 字节数 | 含义 |
---|---|---|
DECIMAL(M, D) / DEC / NUMERIC | M + 2 | 有效范围由M和D决定 |
使用 DECIMAL(M,D) 的方式表示高精度小数。其中,M 被称为精度,D 被称为标度。0 <= M <= 65,0 <= D <= 30,D < M。
例如,定义 DECIMAL(5, 2) 的类型,表示该列取值范是 -999.99 ~ 999.99 。
举个例子:
创建一张表,字段为 2 个定点类型,一个没指定精度和标度,另一个则指明了:
CREATE TABLE test_decimal1(
f1 DECIMAL,
f2 DECIMAL(5, 2)
);
查询结果:
红框中的小括号里的精度和标度在 MySQL 8.0 中同样也会显示,因为 DECIMAL 是需要精确的。
然后往表 test_decimal1
中插入数据并查询:
INSERT INTO test_decimal1(f1)
VALUES(123), (123.45);
SELECT *
FROM test_decimal1;
查询结果:
可以看到,由于默认情况下 DECIMAL 为 (10, 0) 没有小数位,所以对有小数位的数据进行四舍五入操作。
接下来往字段 f2
中插入数据:
INSERT INTO test_decimal1(f2)
VALUES(999.99), (67.567);
SELECT *
FROM test_decimal1;
查询结果:
可以看到数据 67.567
由于小数位超出标度因此被四舍五入了。
再插入数据:
INSERT INTO test_decimal1(f2)
VALUES(1267.567);
输出:
错误代码: 1264
Out of range value for column 'f2' at row 1
可以看到,整数位数超过 (M - D) 时就会报错。
同理,因小数位四舍五入操作导致整数位超过 (M - D) 时也会报错:
INSERT INTO test_decimal1(f2)
VALUES(999.995);
输出:
错误代码: 1264
Out of range value for column 'f2' at row 1
这里用一个简单的例子来体现两种数据类型精度的差别:计算三个小数之和:0.47、0.44、0.19 。正确的答案应该是 1.10 。接下来我们分别查看浮点类型和定点类型的计算精度表现。
# 建表
CREATE TABLE test_double(
f1 DOUBLE
);
# 插入数据
INSERT INTO test_double
VALUES(0.47), (0.44), (0.19);
# 计算总和
SELECT SUM(f1)
FROM test_double;
输出:
SELECT SUM(f1) = 1.1, 1.1 = 1.1
FROM test_double;
输出:
# 建表
CREATE TABLE test_decimal(
f1 DECIMAL(5, 2)
);
# 插入数据
INSERT INTO test_decimal
VALUES(0.47), (0.44), (0.19);
# 计算总和
SELECT SUM(f1)
FROM test_decimal;
输出:
SELECT SUM(f1) = 1.1, 1.1 = 1.1
FROM test_decimal;
输出:
通过上面两个例子可以得出结论,定点类型的精度要高于浮点类型。
由于 DECIMAL 数据类型的精准性,在我们的项目中,除了极少数(比如商品编号)用到整数类型
外,其他的数值都用的是 DECIMAL,原因就是这个项目所处的零售行业,要求精准,一分钱也不能
差。
位类型 BIT 存储的是二进制值。
二进制字符串类型 | 长度 | 长度范围 | 占用空间 |
---|---|---|---|
BIT(M) | M | 1 <= M <= 64 | (M+7)/8 个字节 |
BIT类型,如果没有指定(M),默认是1位。这个1位,表示只能存1位的二进制值。这里(M)是表示二进制的位数,位数最小值为1,最大值为64。
举个例子,创建一张表 test_bit1
,由三个字段组成,数据类型都是 BIT ,如下代码所示:
CREATE TABLE test_bit1(
f1 BIT,
f2 BIT(5),
f3 BIT(64)
);
查询结果:
往里面添加数据:
INSERT INTO test_bit1(f1)
VALUES(0), (1);
SELECT *
FROM test_bit1;
查询结果:
BIT 类型在实际开发中使用不多,作了解即可。
类型 | 名称 | 字节 | 日期格式 | 最小值 | 最大值 |
---|---|---|---|---|---|
YEAR | 年 | 1 | YYYY或YY | 1901 | 2155 |
TIME | 时间 | 3 | HH:MM:SS | -838:59:59 | -838:59:59 |
DATE | 日期 | 3 | YYYY-MM-DD | 1000-01-01 | 9999-12-03 |
DATATIME | 日期时间 | 8 | YYYY-MM-DD HH:MM:SS |
1000-01-01 00:00:00 |
9999-12-31 23:59:59 |
TIMESTAMP | 日期时间 | 4 | YYYY-MM-DD HH:MM:SS |
1970-01-01 00:00:00 UTC |
2038-01-19 03:14:07 UTC |
年份 YEAR 有两种表示方式,分别是 4 位格式的 YYYY 和 2 位格式的 YY 。由于 2 位格式的表示方式过于繁琐和恶心,因此从 MySQL 5.5.27 开始就不推荐使用 2 位格式了。即 YEAR 默认格式就是 4 位的 YYYY ,因此没有必要再显式地声明成 YEAR(4)
了。
举个例子:
-- 建表
CREATE TABLE test_year(
f1 YEAR,
f2 YEAR(4)
);
-- 显示表属性
DESC test_year;
输出:
可以看到,即使不显式地声明 YEAR 为 4 位格式,MySQL 5.7 还是默认设置为 4 位格式。
接下来插入年份数据,有两种方式,分别是以字符串形式 (推荐) 插入和以数值插入,MySQL 都会隐式转换成对应的日期时间类型。
-- 插入数据
INSERT INTO test_year(f1)
VALUES('2017'), (2021);
-- 查询
SELECT *
FROM test_year;
查询结果:
超过 YEAR 表示范围会报错。
-- 插入最大值,成功
INSERT INTO test_year(f1)
VALUES('2155');
-- 超过最大值,失败
INSERT INTO test_year(f1)
VALUES('2156');
输出:
错误代码: 1264
Out of range value for column 'f1' at row 1
DATE 的标准格式是 YYYY-MM-DD ,插入时也可以使用非标准格式 YYYYMMDD ,其会自动隐式转换为标准格式。
举个例子:
-- 建表
CREATE TABLE test_date(
f1 DATE
);
-- 显示表属性
DESC test_date;
输出:
使用 3 种方式插入 DATE 数据:
-- 插入数据
INSERT INTO test_date
VALUES('2017-09-01'), ('20210730'), (20140618);
-- 查询
SELECT *
FROM test_date;
查询结果:
【注意】标准格式 YYYY-MM-DD 只能以单引号形式插入,去掉单引号插入会报错。如下代码所示:
-- 错误的标准格式
INSERT INTO test_date
VALUES(2018-10-09);
输出:
错误代码: 1292
Incorrect date value: '1999' for column 'f1' at row 1
【结论】以后统一用单引号的标准格式:'YYYY-MM-DD'
。
-- 使用单行函数插入当前日期
INSERT INTO test_date
VALUES(CURDATE()), (CURRENT_DATE()), (NOW());
--查询
SELECT *
FROM test_date;
查询结果:
时间类型 TIME 的标准格式是 ‘HH:MM:SS’ 。
TIME 的表示范围之所以会超过 24 小时,能表示 -838:59:59 ~ 838:59:59 这么宽的范围,是因为 TIME 还能表示一段时间间隔。
举个例子:
-- 建表
CREATE TABLE test_time(
f1 TIME
);
-- 显示表属性
DESC test_time;
输出:
TIME 支持以下格式:
‘D HH:MM:SS’ | ‘HH:MM:SS’ | ‘HH:MM’ | ‘D HH:MM’ | ‘D HH’ | ‘SS’ |
---|---|---|---|---|---|
‘HHMMSS’ | HHMMSS | MMSS |
举个例子:
-- 插入TIME数据
INSERT INTO test_time
VALUES
('2 12:30:29'), ('12:35:29'), ('12:40'), ('2 12:40'), ('1 05'), ('45');
INSERT INTO test_time
VALUES('123520'), (124011), (1210);
-- 查询
SELECT *
FROM test_time;
查询结果:
注意,虽然函数 NOW() 是包含了日期和时间,但是会 MySQL 会自动只截取时间部分。
-- 三种单行函数插入TIME数据
INSERT INTO test_time
VALUES(CURTIME()), (CURRENT_TIME()), (NOW());
-- 查询
SELECT *
FROM test_time;
查询结果:
DATETIME 数据类型是日期时间类型中占据存储空间最大的类型,需要占据 8 个字节的空间。它能同时记录日期和时间,因此是实际开发中使用最多的日期时间类型。
其标准格式为 ‘YYYY-MM-DD HH:MM:SS’ 。
举个例子:
-- 建表
CREATE TABLE test_datetime(
f1 DATETIME
);
-- 显示表属性
DESC test_datetime;
输出:
插入时最推荐使用标准格式 ‘YYYY-MM-DD HH:MM:SS’ ,可读性是最好的,别整其他花里胡哨的什么少写一点偷懒,到最后麻烦的还是自己。
-- 三种单行函数插入DATETIME数据
INSERT INTO test_datetime
VALUES
('2021-01-01 06:50:30'),
('20210101065030'),
(19990101000000);
-- 查询
SELECT *
FROM test_datetime;
查询结果:
-- 五种单行函数插入DATETIME数据
INSERT INTO test_datetime
VALUES
(NOW()), (SYSDATE()), (CURRENT_TIMESTAMP()),
(LOCALTIME()), (LOCALTIMESTAMP());
-- 查询
SELECT *
FROM test_datetime;
查询结果:
TIMESTAMP 是时间戳,底层存储的是毫秒数。其标准格式与 DATETIME 相同,都是 ‘YYYY-MM-DD HH:MM:SS’ 。区别是,TIMESTAMP 只占 4 个字节,与 8 字节的 DATETIME 相比,其表示范围要更窄,只能表示 1970-01-01 00:00:00 UTC ~ 2038-01-19 03:14:07 UTC 的时间范围,插入的日期时间若超过该范围就会报错。
插入时最推荐使用标准格式 ‘YYYY-MM-DD HH:MM:SS’ ,可读性是最好的。也可以使用数值 YYYYMMDDHHMMSS
插入,MySQL会自动进行隐式转换,但是可读性稍差。
-- 建表
CREATE TABLE test_timestamp(
f1 TIMESTAMP
);
-- 显示表属性
DESC test_timestamp;
输出:
-- 插入TIMESTAMP数据
INSERT INTO test_timestamp
VALUES('2018-11-23 20:30:30'),
(20210701084500);
-- 查询
SELECT *
FROM test_timestamp;
查询结果:
下面这个例子将对比 DATETIME 和 TIMESTAMP 在时区变化时,其是否会作出相应变化的试验。
-- 建表
CREATE TABLE temp_time(
d1 DATETIME,
d2 TIMESTAMP
);
-- 查看表属性
DESC temp_time;
输出:
-- 插入数据
INSERT INTO temp_time
VALUES
('2021-09-02 14:30:12', '2021-09-02 14:30:12'),
(NOW(), NOW());
-- 查看
SELECT *
FROM temp_time;
查询结果:
-- 修改时区
SET time_zone = '+9:00';
-- 再次查看
SELECT *
FROM temp_time;
查询结果:
结论
存储数据的时候需要对当前时间所在的时区进行转换,查询数据的时候再将时间转换回当前的时区。因此,使用TIMESTAMP存储的同一个时间值,在不同的时区查询时会显示不同的时间。
MySQL中字符串类型的汇总表格如下:
类型 | 特点 | 长度 | 长度范围 | 所占空间 |
---|---|---|---|---|
CHAR(M) | 固定长度 | M | 0 <= M <= 255 | M Bytes |
VARCHAR(M) | 可变长度 | M | 0 <= M <= 65535 | (实际长度+1) Bytes |
+1
的字节就是用来记录当前一共占用了多少个长度。类型 | 特点 | 空间上 | 时间上 | 适用场景 |
---|---|---|---|---|
CHAR(M) | 固定长度 | 浪费存储空间 | 效率高 | 存储不大,速度要求高 |
VARCHAR(M) | 可变长度 | 节省存储空间 | 效率低 | 非CHAR的情况 |
情况1:存储很短的信息。比如门牌号码101,201……这样很短的信息应该用char,因为varchar还要占1个 byte 用于存储信息长度,本来打算节约存储的,结果得不偿失。
情况2:固定长度的。比如使用 UUID 作为主键,那用 CHAR 更合适。因为它固定长度,VARCHATR 动态根据长度的特性就消失了,而且还要占1个长度信息。
情况3:十分频繁改变的column。因为varchar每次存储都要有额外的计算,得到长度等工作,如果一个非常频繁改变的,那就要有很多的精力用于计算,而这些对于char来说是不需要的。
情况4:具体存储引擎中的情况:
在 MySQL 中,TEXT 是用来存储文本数据的类型。例如帖子,新闻文字,时事评论、文章。
类型 | 特点 | 长度 | 长度范围 | 所占空间 |
---|---|---|---|---|
TINYTEXT | 小文本、可变长度 | L | 0 <= L <= 255 | L + 2 字节 |
TEXT | 文本、可变长度 | L | 0 <= L <= 65535 | L + 2 字节 |
MEDIUMTEXT | 中等文本、可变长度 | L | 0 <= L <= 16777215 | L + 3 字节 |
LONGTEXT | 大文本、可变长度 | L | 0 <= L <= 4294967295 (4GB) | L + 4 字节 |
**由于实际存储的长度不确定,MySQL 不允许 TEXT 类型的字段作主键。**遇到这种情况,只能采用 CHAR(M) 或 VARCHAR(M) 。
举个例子:
-- 建表
CREATE TABLE test_text1(
tx TEXT
);
-- 查看表属性
DESC test_text1;
输出:
-- 插入数据
INSERT INTO test_text1
VALUES('Ocean University of China'); -- 有3个空格
-- 查询
SELECT *
FROM test_text1;
查询结果:
-- 查询文本长度
SELECT CHAR_LENGTH(tx)
FROM test_text1;
查询结果:
可见,TEXT 统计长度时,是把空格也计算在内的。这也符合文本数据的需求。
【开发中的经验】
由于 VARCHAR 最多存储 21845 个汉字,因此当汉字数超过 21845 时,就可以考虑使用 TEXT 来存储了。
TEXT 文本类型,可以存比较大的文本段,搜索速度稍慢,因此如果不是特别大的内容,建议使用CHAR,VARCHAR来代替。
还有TEXT类型不用加默认值,加了也没用。而且text和blob类型的数据删除后容易导致 “空洞”,使得文件碎片比较多,所以频繁使用的表不建议包含TEXT类型字段,建议单独分出去,单独用一个表。
ENUM 也称枚举类型。当声明字段为 ENUM 类型时,只允许从成员中选取单个值,不能一次选取多个值。其所需要的存储空间由定义 ENUM 类型时指定的成员个数决定。
类型 | 长度 | 长度范围 | 所占空间 |
---|---|---|---|
ENUM | L | 1 <= M <= 65535 | 1或2 Bytes |
举个例子:
-- 建表
CREATE TABLE test_enum(
season ENUM('春', '夏', '秋', '冬', 'unknow')
);
-- 查看表属性
DESC test_enum;
输出:
-- 只能插入声明的成员,且忽略大小写
INSERT INTO test_enum
VALUES('冬'), ('夏');
INSERT INTO test_enum
VALUES('UNKNOW');
-- 查询
SELECT *
FROM test_enum;
查询结果:
也可以插入对应的索引值,从 1 开始:
INSERT INTO test_enum
VALUES('1'), (4);
SELECT *
FROM test_enum;
查询结果:
没有限制非空的约束下,可以添加 NULL 值。
插入非枚举成员的值会报错:
-- 插入非枚举成员
INSERT INTO test_enum
VALUES('人');
输出:
错误代码: 1265
Data truncated for column 'season' at row 1
一次选取多个成员值也会报错:
INSERT INTO test_enum
VALUES('夏', '秋');
输出:
错误代码: 1136
Column count doesn't match value count at row 1
上面提到,ENUM 插入数据时只能选取一个成员值。而 SET 则可以一次选取多个成员值。但 SET 的成员个数的上限为 64 。
成员个数范围(L表示实际成员个数) | 所占空间 |
---|---|
1 <= L <= 8 | 1字节 |
9 <= L <= 16 | 2字节 |
17 <= L <= 24 | 3字节 |
25 <= L <= 32 | 4字节 |
33 <= L <= 64 | 8字节 |
从上表可以看出,SET 类型成员个数越多,其占用的存储空间越大。
举个例子:
-- 建表
CREATE TABLE test_set(
s SET('A', 'B', 'C')
);
-- 查看表属性
DESC test_set;
输出:
-- 插入数据
INSERT INTO test_set(s)
VALUES('A'), ('A,B'); -- 不能有空格
-- 查询
SELECT *
FROM test_set;
查询结果:
INSERT INTO test_set(s)
VALUES('A,B,C,A');
-- 查询
SELECT *
FROM test_set;
查询结果:
INSERT INTO test_set(s)
VALUES('A,B,C,D');
-- 查询
SELECT *
FROM test_set;
查询结果:
错误代码: 1265
Data truncated for column 's' at row 1
MySQL 除了存储可读性的文本、数值等数据,还可以存储如图片、音频、视频等二进制数据。
BINARY(M) 与 VARBINARY(M) 之间的关系与前面的 CHAR 与 VARCHAR 是类似的,其特性表格如下所示:
类型 | 特点 | 长度 | 长度范围 | 所占空间 |
---|---|---|---|---|
BINARY(M) | 固定长度 | M | 0 <= M <= 255 | M Bytes |
VARBINARY(M) | 可变长度 | M | 0 <= M <= 65535 | (实际长度+1) Bytes |
0
。举个例子:
-- 建表
CREATE TABLE test_binary(
f1 BINARY,
f2 BINARY(3),
f3 VARBINARY(10)
);
-- 查看表属性
DESC test_binary;
输出:
由于一个英文字符占 1 字节大小,因此我们插入英文字符来举例。
-- 插入数据
INSERT INTO test_binary(f1, f2)
VALUES('a', 'abc');
-- 查询
SELECT *
FROM test_binary;
查询结果:
-- 插入数据
INSERT INTO test_binary(f2, f3)
VALUES('ab', 'ab');
-- 查询
SELECT *
FROM test_binary;
查询结果:
查询字段 f2 和 f3 的长度:
SELECT LENGTH(f2), LENGTH(f3)
FROM test_binary;
查询结果:
可以看到,同样是插入 2 字节大小的英文字符串 ‘ab’ ,VARBINARY 的长度正常显示为 2 ,BINARY 长度为 3 ,这进一步验证了 BINARY 数据长度小于声明长度是会自动填充 0
直至满足声明长度。
BLOB 类型是用来存储大二进制对象的,如图片、音频、视频,其也是可变长度的。
【注意】
虽然 MySQL 具备存储图片、音频、视频二进制文件的能力,但在实际开发中并不会使用 MySQL 来存储图片、音频、视频。而是直接把图片、音频、视频直接存储在服务器的硬盘上,在把这些资源对应的存储路径 (或URL地址) 存储在 MySQL 中。
类型 | 特点 | 长度 | 长度范围 | 所占空间 |
---|---|---|---|---|
TINYBLOB | 小二进制对象、可变长度 | L | 0 <= L <= 255 | L + 2 字节 |
BLOB | 二进制对象、可变长度 | L | 0 <= L <= 65535 | L + 2 字节 |
MEDIUMBLOB | 中等二进制对象、可变长度 | L | 0 <= L <= 16777215 | L + 3 字节 |
LONGBLOB | 大二进制对象、可变长度 | L | 0 <= L <= 4294967295 (4GB) | L + 4 字节 |
举个例子:
-- 建表
CREATE TABLE test_blob(
id INT,
img MEDIUMBLOB
);
-- 查看表属性
DESC test_blob;
输出:
想要通过浏览器端/客户端上传图片,经过后端服务器存储到 MySQL 数据库中,需要应用到 IO 流和网络传输的知识。这里就不写 Java 代码演示了,仅用 SQLyog 中图形化方式插入图片:
-- 插入ID数据
INSERT INTO test_blob(id)
VALUES(1001);
-- 查询
SELECT *
FROM test_blob;
查询结果:
然后插入图片,先在 SQLyog 侧边栏选中表 test_blob
,再点击 4 表数据
:
然后点击字段 img
下想要插入图片的表格,SQLyog 就会弹出一个图形化窗口,通过此窗口就可以图形化地插入图片:
最后点击保存,提交到数据库:
提交以后再次查询数据库,是看不了图片这种非可读性的二进制文件的,可以通过 Java 中的输出流来读取。
在使用 TEXT 和 BLOB 字段类型时要注意以下几点,以便更好的发挥数据库的性能。
① BLOB 和 TEXT 值也会引起自己的一些问题,特别是执行了大量的删除或更新操作的时候。删除这种值
会在数据表中留下很大的 “空洞” ,以后填入这些 “空洞” 的记录可能长度不同。为了提高性能,建议定期
使用 OPTIMIZE TABLE 功能对这类表进行碎片整理。
② 如果需要对大文本字段进行模糊查询,MySQL 提供了前缀索引 (下篇讲解) 。但是仍然要在不必要的时候避免检索大型的 BLOB 或 TEXT 值。例如,SELECT * 查询就不是很好的想法,除非你能够确定作为约束条件的 WHERE 子句只会找到所需要的数据行。否则,你可能毫无目的地在网络上传输大量的值。
③ 把 BLOB 或 TEXT 列分离到单独的表中。在某些环境中,如果把这些数据列移动到第二张数据表中,可
以让你把原数据表中的数据列转换为固定长度的数据行格式,那么它就是有意义的。这会减少主表中的
碎片,使你得到固定长度数据行的性能优势。它还使你在主数据表上运行 SELECT * 查询的时候不会通过
网络传输大量的 BLOB 或 TEXT 值。
JSON (JavaScript Object Notation) ,是一种轻量级的数据交换格式。用于在浏览器端/客户端与服务器端之间的数据交换。由于 JSON 具有更加简洁和清晰的层次结构,现已逐渐代替 XML 。
JSON 的本质就是带有固定格式的字符串。
举个例子:
-- 建表
CREATE TABLE test_json(
js json
);
-- 查看表属性
DESC test_json;
输出:
-- 插入JSON数据
INSERT INTO test_json(js)
VALUES('{"name":"XieSihang", "age":23, "address":{"province":"guangdong", "city":"zhaoqing"}}');
-- 查询
SELECT *
FROM test_json;
查询结果:
MySQL 还支持提取和解析 JSON 中的信息。
SELECT js -> '$.name' AS `name`,
js -> '$.age' AS age,
js -> '$.address.province' AS province,
js -> '$.address.city' AS city
FROM test_json;
查询结果:
空间类型在后端开发中使用非常少,只在地图开发等场景中才会用到。因此仅作了解即可。
在定义数据类型时,整数用 INT,小数用定点数类型 DECIMAL(M, D) ,日期时间用 DATETIME 。
这样做的好处是,首先确保你的系统不会因为数据类型定义出错。不过,凡事都是有两面的,可靠性
好,并不意味着高效。比如,TEXT 虽然使用方便,但是效率不如 CHAR(M) 和 VARCHAR(M)。
关于字符串的选择,建议参考如下阿里巴巴的《Java开发手册》规范:
阿里巴巴《Java开发手册》之MySQL数据库: