MySQL 的类型是「关卡」,这里的类型是「标签」。 教材把类型分四组:数值、字符串、日期时间、
二进制;而本沙箱只认五种存储类,长度和格式都不强制。
Demo 撞出三件事:16 个字符塞进 VARCHAR(10) 照收(这里不拦,MySQL 报 Data too long);
DECIMAL(8,2) 存 0.1 加 0.2 得 0.30000000000000004(这里是浮点,MySQL 是定点);
日期 2026/04/02 也照收(这里当字符串存)。
上一篇讲语法, 这一篇讲每个字段该挂什么类型——它决定三件事:能存什么、占多少空间、算出来准不准。 也是全课差异最大的一篇:MySQL 有二十多种类型,SQLite 只有五种存储类, 所以这一篇的每个结论都要连引擎一起说。
一、数值:整数分宽度,小数分定点与浮点
整数五种宽度:TINYINT(1 字节,-128~127)、SMALLINT(2 字节)、MEDIUMINT(3 字节)、
INT(4 字节)、BIGINT(8 字节)。 按取值范围挑最小的那个,索引更小、更快。
UNSIGNED 去掉负数那一半,正数上限翻倍;
ZEROFILL 补前导零(INT(4) ZEROFILL 存 7 显示 0007)——
这个只在显示时起作用,8.0 已不推荐。
小数分两种,这一点必须分清:DECIMAL(p,s) 是定点数,p 是总位数、s 是小数位,
存 0.1 就是 0.1,算钱必须用它;FLOAT/DOUBLE 是二进制浮点,快但算不准。
DECIMAL(8,2) 的意思:一共 8 位数字、其中 2 位是小数,
所以整数部分最多 6 位,最大 999999.99。
二、字符串:定长与变长,差在「补不补空格」
CHAR(n) 定长:存 4 个字符的 AB 会补两个空格,取出来时又自动删掉,
所以永远读不到尾随空格。 适合长度固定的(性别、省份代码、身份证)。
VARCHAR(n) 变长:按实际长度存,多一个字节记长度。
n 是「字符数」不是字节数——utf8mb4 下一个汉字算一个字符,
但它占 4 个字节,所以 VARCHAR(255) 最坏要 1020 字节。
TEXT/LONGTEXT 放长文本,
不能有默认值、索引只能前缀索引。 ENUM 是枚举(只能取列出的几个值)、
SET 是集合(可多选)。
选型口诀:
长度固定用 CHAR,其余用 VARCHAR,超过几千字用 TEXT。 别一律 VARCHAR(255)——它占索引空间,
也掩盖了「这个字段到底多长」的信息。
三、日期时间:四种类型,一个 2038 的坑
DATE 只存日期(2026-04-10)、TIME 只存时间(10:30:00)、
DATETIME 存日期加时间(8 字节,不随时区变)、TIMESTAMP 也存日期加时间(4 字节,
存的是 UTC,取出来按会话时区换算)。
TIMESTAMP 的范围只到 2038-01-19(4 字节有符号整数装不下更大的秒数),
这就是著名的「2038 问题」。 长期存时间用 DATETIME,别用 TIMESTAMP。
YEAR 只存年份(1 字节)。 8.0 起 DATETIME 可以带小数秒:DATETIME(3) 精确到毫秒。
四、二进制:放文件的那几种
BINARY(n)/VARBINARY(n) 是定长/变长的二进制串,
BLOB 系列(TINYBLOB/BLOB/MEDIUMBLOB/LONGBLOB)放图片、文件。
实务上不建议把图片塞数据库:备份变大、查询变慢、连接被占。
常规做法是文件存磁盘或对象存储,库里只存路径。
什么时候真该用:数据量和库一起走、要事务一致(比如电子签章的原文哈希)、
文件小且必须原子写入。 除此之外都存路径。
五、本沙箱的类型:五种存储类,长度不强制
SQLite 只有五种存储类:NULL、INTEGER、REAL、TEXT、BLOB。
VARCHAR(10) 里的 10 只是「亲和性提示」,不拦长度。
Demo 实测两件事:16 个字符塞进 VARCHAR(10) 照收;
DECIMAL(8,2) 存成了 real,0.1 + 0.2 得 0.30000000000000004。
所以本课的建表语句照 MySQL 的类型写(这样你搬到真库上不用改),
但要知道这里只是「认这个名字」,不照它执行。 要算准钱,先乘 100 转整数——
Demo 最后一段就是这么做的。
六、小结
教材小结:数据类型分数值、字符串、日期时间、二进制四组;整数按范围挑宽度,钱用 DECIMAL,
字符串分 CHAR 与 VARCHAR,时间分 DATE/TIME/DATETIME/TIMESTAMP。
本课补三条:一是长度和类型在这里不强制,MySQL 会拦;
二是 DECIMAL 在这里退化成浮点,算钱要先转整数;三是日期在这里是字符串,格式靠自己保证。
七、动手 Demo:五种类型各撞一次墙
Demo 六段:按 MySQL 写法建一张五种类型的表,然后依次撞:超长字符串、小数精度、非标准日期格式, 最后给一张 MySQL 与 SQLite 的类型对照表。 这一篇的每一段都在演示「这里为什么不拦你」——拦不拦是引擎的差别,该定什么类型是你的决定。
import sqlite3
c = sqlite3.connect(":memory:")
c.execute("""CREATE TABLE t_types (
id INTEGER PRIMARY KEY,
code VARCHAR(10),
flag CHAR(4),
price DECIMAL(8,2),
made_on DATE)""")
# 16 个字符塞进 VARCHAR(10)
c.execute("INSERT INTO t_types (code, flag, price, made_on) "
"VALUES ('ABCDEFGHIJKLMNOP', 'AB', 9.90, '2026-04-01')")
c.execute("SELECT code, length(code) FROM t_types")
# 日期格式随便给也收
c.execute("INSERT INTO t_types (code, flag, price, made_on) "
"VALUES ('X1', 'CD', 1.00, '2026/04/02')")
c.execute("SELECT made_on, typeof(made_on), date(made_on) FROM t_types")
# 钱:DECIMAL 在这是浮点
c.execute("CREATE TABLE t_money (id INTEGER PRIMARY KEY, amount DECIMAL(8,2))")
c.execute("INSERT INTO t_money (amount) VALUES (0.1), (0.2)")
c.execute("SELECT CAST(SUM(amount) AS TEXT), SUM(amount) = 0.3 FROM t_money")
c.execute("SELECT SUM(CAST(ROUND(amount * 100) AS INTEGER)) / 100.0 FROM t_money")
实跑输出:
--------------------------------------------------------------------
第七课:数据类型,以及这个引擎为什么不拦你
--------------------------------------------------------------------
照 MySQL 的写法建一张表:五种类型都用上
SQL> CREATE TABLE t_types ( id INTEGER PRIMARY KEY, code VARCHAR(10), flag CHAR(4),
price DECIMAL(8,2), made_on DATE)
完成
**这五个类型名 MySQL 有,SQLite 只是「认这个名字」,并不照它执行。**
塞一个超长的值:MySQL 会拦,这里照收
SQL> INSERT INTO t_types (code, flag, price, made_on) VALUES ('ABCDEFGHIJKLMNOP', 'AB', 9.90,
'2026-04-01')
影响 1 行
SQL> SELECT code, length(code) AS chars FROM t_types
code chars
---------------------------
ABCDEFGHIJKLMNOP 16
(1 行)
**`VARCHAR(10)` 这次没拦住 16 个字符——SQLite 的长度声明只是「亲和性提示」,不做强制。**
MySQL 在严格模式下会直接报 `Data too long for column`。
**这条差异很要紧**:在本沙箱能跑过的建表语句,搬到 MySQL 上可能插不进去。
**所以长度还是要照 MySQL 的规矩定。**
DECIMAL 在这里退化成浮点:钱算不准的那 0.01
SQL> SELECT amount, typeof(amount) AS store_type FROM t_money
amount store_type
----------------------
0.10 real
0.20 real
(2 行)
SQL> SELECT CAST(SUM(amount) AS TEXT) AS total_raw,
SUM(amount) = 0.3 AS is_exactly_03 FROM t_money
total_raw is_exactly_03
--------------------------------------
0.30000000000000004 0
(1 行)
**`total_raw` 那一列露出了真面目:0.30000000000000004。**
**它和 0.3 不相等(`is_exactly_03` 给 0),只是平时按两位小数显示看不出来。**
**原因是这里的 `DECIMAL(8,2)` 存成了二进制浮点(`typeof` 给 `real`)。**
**MySQL 的 DECIMAL 是定点数,存 0.1 就是 0.1,不会出这个误差。**
**所以在 MySQL 里「钱用 DECIMAL」是硬道理;在本沙箱要算准,得先乘 100 转成整数算。**
用整数分算钱:把误差消掉
SQL> SELECT SUM(CAST(ROUND(amount * 100) AS INTEGER)) / 100.0 AS total_yuan FROM t_money
total_yuan
-----------
0.30
(1 行)
**先乘 100 变整数、加起来、再除回去,结果就是 0.30。**
**这是所有语言的通用解法,不是 SQL 的技巧。**
日期在这里就是一个字符串
SQL> INSERT INTO t_types (code, flag, price, made_on) VALUES ('X1', 'CD', 1.00, '2026/04/02')
影响 1 行
SQL> SELECT made_on, typeof(made_on) AS store_type,
date(made_on) AS parsed FROM t_types WHERE code = 'X1'
made_on store_type parsed
-----------------------------------------
2026/04/02 text NULL
(1 行)
**`typeof` 给 `text`——日期是当字符串存的,格式没人管,`2026/04/02` 也照收。**
**MySQL 的 DATE 只认 `YYYY-MM-DD`,别的格式直接报错或给 0000-00-00。**
**在本沙箱要算日期差,得靠 `date()` 这类函数自己解析。**
所以这张对照表要记住
分类 MySQL 的类型 本沙箱(SQLite)
---------------------------------------------------------------------
整数 tinyint/int/bigint(分宽度) 一律 INTEGER
小数 decimal 定点,float/double 浮点 REAL(浮点)
字符串 char 定长、varchar 变长、text 长文本 TEXT,长度不强制
日期 date/time/datetime/timestamp TEXT,格式自己保证
二进制 binary/varbinary/blob BLOB
(5 行)
**一句话:MySQL 的类型是「关卡」,SQLite 的类型是「标签」。**
**所以本课的建表语句照 MySQL 写,跑起来的数据由 SQLite 管,差异逐处标出。**
第一段建表。 VARCHAR(10)、CHAR(4)、DECIMAL(8,2)、DATE 都写上了,
MySQL 照这样建没问题,这里只是「认名字」。
第二段超长。 16 个字符进了 VARCHAR(10),
length() 量出来 16——MySQL 严格模式这里会直接报错。
第三段小数。 typeof 给 real,说明 DECIMAL 退化成了浮点;
0.1 加 0.2 得 0.30000000000000004,和 0.3 不相等。
第四段转整数算。 乘 100、求和、再除回去,得 0.30——这是所有语言处理钱的通用办法。
第五段日期。 2026/04/02 照收,typeof 给 text,date() 解析不了就给 NULL。
第六段对照表。 五类逐一对照,一句话收尾:MySQL 的类型是关卡,SQLite 的类型是标签。
30 秒实验
- 把
VARCHAR(10)改成VARCHAR(5),16 个字符还进得去吗?为什么? - (先想再跑)
SELECT typeof(9.90), typeof(9)分别给什么? - 用
CAST(amount * 100 AS INTEGER)求和,为什么比直接 SUM 准?
常见误区
- 一律用
VARCHAR(255)。 占索引空间、掩盖真实长度,还容易让人误以为「够长了」。 - 钱用 FLOAT/DOUBLE。 浮点存不准,0.1 加 0.2 都不等于 0.3;钱必须 DECIMAL。
- 时间用 TIMESTAMP。 2038 年就到头了,而且随时区换算,跨时区系统里最容易错。
自测三问(点开对答案)
1. DECIMAL 和 FLOAT 的根本区别是什么? —— DECIMAL 是定点数,按十进制位存,0.1 就是 0.1; FLOAT/DOUBLE 是二进制浮点,很多十进制小数存不精确,算钱会出误差。 2. CHAR 和 VARCHAR 差在哪? —— CHAR 定长,不足补空格、取出时删掉;VARCHAR 变长,按实际长度存,另存一个长度字节。 长度固定用 CHAR,其余用 VARCHAR。 3. 为什么本沙箱能往 VARCHAR(10) 里塞 16 个字符? —— 因为 SQLite 的长度声明只是类型亲和性提示,不强制;MySQL 在严格模式下会报 Data too long。类型定完了, 下一篇是最不起眼但天天要用的东西:注释。 它不只是给人看的,还是调试时最快的一把刀。
- ☐ (本篇完,下一篇 2.3 注释)