数据类型

SQL 标准支持多种固有类型。

每种类型都可能包含一个被称作空值的特殊值。空值表示一个缺失的值,该值可能存在但并不为人所知,或者可能根本不存在。在可能的情况下,我们希望禁止加入空值,正如我们马上将看到的那样。

文本字符串类型

文本字符串类型大小(Bytes)用途
CHAR0-255定长字符串
VARCHAR0-65535可变长字符串
TINYTEXT0-255短文本字符串
TEXT0-65535长文本数据
MEDIUMTEXT0-16777215中等长度文本数据
LONGTEXT0-4294967295极大文本数据
TINYBLOB0-255不超过 255 个字符的二进制字符串
BLOB0-65535二进制形式的长文本数据
MEDIUMBLOB0-16777215二进制形式的中等长度文本数据
LONGBLOB0-4294967295二进制形式的极大文本数据

CHAR 和 VARCHAR

Q: CHAR 和 VARCHAR 的区别?

对于 CHAR(M) 类型:

  1. CHAR(M) 类型一半需要预先定义字符串长度,如果不指定 (M),则表示长度默认是 1 个字符
  2. 如果保存时,数据的实际长度比 CHAR 类型声明的长度小,则会在 右侧填充 空格以达到指定的长度。当 MySQL 检索 CHAR 类型的数据时,CHAR 类型的字段会去除尾部的空格
  3. 定义 CHAR 类型字段时,声明的字段长度即为 CHAR 类型字段所占的存储空间的字节数
  4. 该表格在英文情况下是适用的,在中文情况下需要根据 字符集编码 来决定所占存储空间的大小

对于 VARCHAR(M) 类型:

  1. VARCHAR(M) 定义时,必须指定长度 M,否则报错
  2. MySQL4.0 版本以下,VARCHAR(20) 指的是 20 字节,如果存放 UTF8 汉字时,只能存 6 个(每个汉字 3 字节);MySQL5.0 版本以上,VARCHAR(20) 指的是 20 字符
  3. 检索 VARCHAR 类型的字段数据时,会保留数据尾部的空格。VARCHAR 类型的字段所占用的存储空间为字符串实际长度加 1 个字节

两者相同点:

  1. CHAR(n)VARCHAR(n) 中的 n 都代表字符的个数
  2. 超过 CHAR(n)VARCHAR(n) 最大长度 n 的限制后,字符串会被截断

不同点:

  1. CHAR(n) 不论实际存储的字符数多少都会占用 n 个字符的空间,而 VARCHAR(n) 只会占用实际字符应该占用的字节空间加 1(实际长度 length,0<=length<255)或加 2(length>255)。因为 VARCHAR(n) 保存数据时除了要保存字符串之外还会加一个字节来记录长度(如果列声明长度大于 255 则使用两个字节来保存长度)。
  2. 能存储的最大空间限制不一样:CHAR(n) 的存储上限为 255 字节。
  3. CHAR(n) 在存储时会截断尾部的空格,而 VARCHAR(n) 不会。

CHAR(n) 是适合存储很短的、一般固定长度的字符串。例如,CHAR(n) 非常适合存储密码的 MD5 值,因为这是一个定长的值。对于非常短的列,CHAR(n)VARCHAR(n) 在存储空间上也更有效率。

哪些情况适用 CHARVARCHAR 更好?

  1. 情况一:存储很短的信息。例如门牌号码 101201...这样很短的信息应该用 CAHR,因为 VARCHAR 还要占用 Byte 用于存储信息长度, 本来打算节约存储的,结果得不偿失
  2. 情况二:固定长度的。比如使用 UUID 作为主键,那用 CHAR 应该更合适。因为他固定长度,VARCHAR 动态根据长度的特性就消失了,而且还要占个长度信息
  3. 情况三:十分频繁改变的列。因为 VARCHAR 每次存储都要有额外的计算,得到长度等工作,如果一个非常频繁改变的列,那就要有很多的精力用于计算,而这些对于 CHAR 来说是不需要的
  4. 情况四:具体存储引擎中的情况
  • MyISAM 数据引擎和数据列:MyISAM 数据表,最好使用固定长度(CHAR)的数据列代替可变长度(VARCHAR)的数据列,这样使得整个表静态化,从而使数据检索更快,用空间换时间
  • MEMORY 存储引擎和数据列:MEMORY 数据表目前都使用固定长度的数据行存储,因此无论是用 CHAR 或 VARCHAR 列都没有关系,两者都是作为 CHAR 类型处理
  • InnoDB 存储引擎,建议使用 VARCHAR 类型,因为对于 InnoDB 数据表,内部的行存储格式并没有区分固定长度和可变长度列(所有数据行都使用指向数据列值的头指针),而且主要影响性能的因素时数据行使用的存储总量,由于 CHAR 平均占用的空间多余 VARCHAR,所以除了间断并且固定长度的,其他考虑 VARCHAR,这样节省空间,对磁盘 I/O 和数据存储总量比较好

TEXT

  1. 在向 TEXT 类型的字段保存和查询数据时,系统自动按照实际长度存储,不需要预先定义长度,这一点和 VARCHAR 类型相同
  2. 由于实际存储的长度不确定,MySQL 不允许 TEXT 类型的字段作为主键,遇到这种情况,你只能采用 CHAR(M) 或者 VARCHAR(M)

TEXT 文本类型,可以存比较大的文本段,搜索速度稍慢,因此如果不是特别大的内容,建议使用 CHAR 或 VARCHAR 代替。还有 TEXT 类型不用加默认值,加了也没用。而且 TEXT 和 BLOB 类型的数据删除后容易导致“空洞”,使得文件碎片比较多,所以频繁使用的表不建议包含 TEXT 类型字段,建议单独分出去,单独使用一个表。

字符字节参考表

编码字符一个字母一个中文字符字母标点中文标点
ASCII 码1 字节
UTF-8 编码1 字节3 字节1 字节3 字节
Unicdoe 编码2 字节2 字节2 字节2 字节
GB 2312 编码或 GBK 编码1 字节2 字节1 字节2 字节
UTF-162 字节2 字节2 字节2 字节
UTF-324 字节4 字节4 字节4 字节
Latin11 字节1 字节

数字类型

整数类型

类型有符号无符号大小(Bytes)用途
TINYINT(-128, 127)[0, 255]1非常小的整数值
SMALLINT(-32768, 32767)[0, 65535]2较小的整数值
MEDIUMINT-8388608, 8388607[0, 2^24-1]3中等大小整数值
INT 或 INTEGER(-2 147 483 648,2 147 483 647)[0, 2^32-1]4标准整数
BIGINT[0, 2^64-1]8较大整数值

适用场景:

  • TINYINT:一般用于枚举数据,例如系统设定取值范围很小且固定场景
  • SMALLINT:可以用于较小范围的统计数据,例如统计工厂的固定资产库存数量等
  • MEDIUMINT:用于较大整数的计算,例如车展每日的客流量等
  • INT / INTEGER:取值范围足够大,一般情况下不用考虑超限问题,例如商品编号
  • BIGINT:只有当你处理特别巨大的整数时才会用到,例如双十一的交易量、大型门户网站点击量、证券公司衍生产品持仓等

小数类型

类型有符号无符号大小(Bytes)用途
FLOAT4单精度
浮点数值
DOUBLE8双精度
浮点数值
DECIMAL(M, D)依赖于 M 和 D 的值m可以存储大整数或高精度,可以理解为字符串处理

UNSIGNED

UNSIGNED 属性只针对整数类型,表示无符号的意思(相当于禁用负值)。

取值范围如果加了 UNSIGNED,则最大值翻倍,例如,TINYINT 类型最大是 127,那 TINYINT UNSIGNED 最大就可以到 127 * 2 => 255

ZEROFILL

ZEROFILL 表示令填充(如果某列为 ZEROFILL,那么 MySQL 会自动为当前列添加 UNSIGNED 属性),如果指定了 ZEROFILL 只是表示不够 M 位时,用 0 在左边填充,如果超过 M 位,只要不超过数据存储范围即可。

原来,在 int(M) 中,M 的值跟 int(M) 所占多少存储空间并无任何关系。int(3)int(4)int(8) 在磁盘上都是占用 4 bytes 的存储空间,也就是说,int(M) 必须和 UNSINGED 和 ZEROFILL 一起使用才有意义,如果整数值超过 M 位,就按实际位数存储,只是无须再用字符 0 进行填充。

日期和时间类型

每个时间类型有一个有效值范围和一个”零“值,当指定不合法的 MySQL 不能表示的值时使用”零“值。

类型大小(Bytes)范围格式用途
YEAR11901 / 2155YYYY年份值
TIME3-838:59:59 / 838:59:59HH:MM:SS时间值或持续时间
DATE31000-01-01 / 9999-12-31YYYY-MM-DD日期值
DATETIME81000-01-01 00:00:00 / 9999-12-31 23:59:59YYYY-MM-DD HH:MM:SS混合日期和时间值
TIMESTAMP41970-01-01 00:00:00 / 2038YYYYMMDD HHMMSS混合日期和时间值,时间戳

注意事项:

  • TIMESTAMP:存储数据的时候需要对当前时间所在的时区进行转换,查询数据的时候再将时间转换回当前的时区。因此,使用 TIMSTAMP 存储的同一个时间值,在不同的时区查询时会显示不同的时间
  • 一般存储注册时间、商品发布时间等,不建议使用 DATETIME 存储,而是使用 TIMESTAMP,因为 DATETIME 虽然直观,但不便于计算

参考资料