SQLite 的 STRICT 表:一个容易忽略,却很实用的特性
利用严格类型检查,让数据错误在写入阶段就被发现。
slashslashdev·

SQLite 的类型系统一直和其他关系型数据库不太一样。
在 PostgreSQL 或 MySQL 中,列声明为什么类型,就只能保存对应类型的数据。而 SQLite 默认采用的是一种更宽松的策略,它会尽量帮你完成类型转换,即使转换不了,也不一定会拒绝写入。
这种灵活性让 SQLite 很容易使用,但也给数据一致性留下了一些隐患。
SQLite 3.37.0 引入的 STRICT Tables,就是为了改善这一点。
宽松的类型系统意味着什么?
先看一个简单的例子:
CREATE TABLE users (
age INTEGER
);很多人会认为,这一列只能保存整数。
但在普通 SQLite 表中,下面这条语句仍然可以执行成功:
INSERT INTO users VALUES ('hello');虽然 age 声明的是 INTEGER,SQLite 仍然会把 'hello' 保存进去,最终以 TEXT 的形式存储。
如果插入的是能够转换成整数的字符串:
INSERT INTO users VALUES ('42');SQLite 则会自动转换成整数再保存。
这种行为来自 SQLite 的 Type Affinity(类型亲和性)。字段声明的是一种"倾向",而不是绝对约束。
对于导入历史数据、兼容不同数据来源来说,这种设计非常方便;但如果数据库本身承担的是业务数据存储,过于宽松的约束就可能让一些错误长期隐藏下来。
STRICT Tables 改变了什么?
STRICT Tables 的使用方式很简单,只需要在建表语句最后加上一个关键字:
CREATE TABLE users (
id INTEGER PRIMARY KEY,
age INTEGER
) STRICT;之后,再执行下面这条语句:
INSERT INTO users VALUES (1, 'hello');SQLite 会直接返回错误,不再允许数据写入。
这样一来,类型错误会在数据进入数据库之前就被发现,而不是等到后面的查询、计算或者业务逻辑运行时才暴露出来。
仍然保留了合理的自动转换
STRICT 并没有彻底放弃 SQLite 一贯的灵活性。
如果数据可以无损转换,SQLite 仍然会接受。例如:
INSERT INTO users VALUES (1, '42');这里的 "42" 可以转换为整数,因此能够正常写入。
但对于无法转换的数据:
INSERT INTO users VALUES (1, 'abc');SQLite 就会拒绝执行。
也就是说,STRICT 并不是完全禁止类型转换,而是要求最终保存的数据必须符合字段定义的类型。
STRICT 支持哪些类型?
开启 STRICT 后,可使用的类型名称只有六种:
INT
INTEGER
REAL
TEXT
BLOB
ANY其中:
INT、INTEGER:整数;REAL:浮点数;TEXT:文本;BLOB:二进制数据;ANY:允许保存任意类型。
像很多数据库里常见的 VARCHAR(255)、BOOLEAN、DATE、DECIMAL 等类型,在 STRICT 表中都不能直接使用。
另外,STRICT 还有一个要求:每一列都必须显式声明类型。
例如:
CREATE TABLE users (
name
) STRICT;这条语句会执行失败,因为 name 没有声明数据类型。
迁移到 STRICT 需要注意什么?
STRICT 并不能直接应用到已有的表。
SQLite 目前不支持通过 ALTER TABLE 将普通表改成 STRICT。如果想使用这一特性,通常需要新建一张 STRICT 表,再将符合要求的数据迁移过去。
如果现有数据中已经存在类型不一致的情况,也需要先进行清理,否则迁移过程可能失败。
兼容性也需要考虑
STRICT Tables 是 SQLite 3.37.0 新增的功能。
如果数据库中包含 STRICT 表,那么较早版本的 SQLite 将无法识别这种表定义。因此,在需要兼容旧版本 SQLite 的环境中,应提前确认运行时版本。
对性能有影响吗?
对于大多数应用来说,STRICT 对性能的影响可以忽略不计。
STRICT 主要是在数据写入时增加了一次类型检查。如果写入的数据已经符合字段定义,SQLite 只需要完成一次简单的验证;如果数据能够无损转换,例如将字符串 "42" 转换为整数,也仍然会正常写入。
相比磁盘 I/O、索引维护或事务提交等数据库操作,这部分额外开销通常很小,很少会成为性能瓶颈。
当然,如果应用对写入性能极其敏感,或者每秒需要处理大量数据,仍然建议结合实际业务进行基准测试,再决定是否启用 STRICT。
对于绝大多数业务系统而言,STRICT 带来的数据一致性和更早发现错误的收益,通常远大于它增加的少量类型检查开销。
什么时候适合使用?
如果数据库主要保存应用的业务数据,希望尽可能保证数据质量,那么 STRICT 往往是一个不错的选择。它能够让类型错误更早暴露,也让数据库中的数据更加一致。
当然,SQLite 原本宽松的类型系统并没有因此失去价值。对于数据导入、历史数据兼容或数据清洗这类场景,允许不同类型的数据暂时存在,反而会让处理过程更加灵活。
小结
STRICT Tables 并没有改变 SQLite 的核心设计,而是在保留原有灵活性的基础上,提供了一种更加严格的数据约束方式。
它允许安全的类型转换,却会阻止明显不符合类型要求的数据进入数据库。对于大多数需要长期维护的应用来说,只需在建表时增加一个 STRICT 关键字,就能获得更可靠的数据质量和更早的错误反馈。