SQLite采用一种与大多数SQL数据库截然不同的数据类型处理方式——它使用动态类型系统和类型亲和性(Type Affinity),而非强制的列类型约束。这意味着一列被声明为VARCHAR(100)的字段,完全可以存入一个整数,甚至一个BLOB对象。支撑这种灵活性的是一套清晰的规则,理解它们才是用好SQLite的关键。

在深入规则之前,必须先厘清两个核心概念:存储类(Storage Class)和类型亲和性。SQLite内部实际使用五种存储类:NULL、INTEGER、REAL、TEXT和BLOB。每一个存入数据库的值都必定属于其中一种存储类,这和列声明没有任何关系。类型亲和性则是给列的“建议”,告诉SQLite在插入数据时优先使用哪种存储类,但它不是强制约束。
类型亲和性的五种类型
当你执行CREATE TABLE时,SQLite会根据列声明的类型名推断出该列的亲和性。规则按以下优先级确定:
1. TEXT亲和性:如果类型名中包含字符串“CHAR”、“CLOB”或“TEXT”,则该列具有TEXT亲和性。VARCHAR(255)、NVARCHAR(100)、TINYTEXT等都会被归于这一类。具有TEXT亲和性的列在存储数据时,会倾向于将值转换为文本形式。
2. NUMERIC亲和性:当类型名中包含字符串“BLOB”、“REAL”、“FLOA”或“DOUB”时,会被优先判定为相应的亲和性,但如果都不包含,而包含了“NUM”这个词(如NUMERIC),则该列具有NUMERIC亲和性。更常见的是,声明中包含“INT”但不含“CHAR”时,会优先被下一级INTEGER捕获,但如果在未捕获的情况下,就可能是NUMERIC。实际上,规则是:如果类型名包含“INT”,则指派为INTEGER亲和性(除非被前面的TEXT规则覆盖)。NUMERIC亲和性是一类特殊的混合类型,它尝试将值同时存储为INTEGER或REAL,仅在无法转换时退化为TEXT。
3. INTEGER亲和性:类型名包含字符串“INT”(且不被TEXT规则覆盖)时,该列具有INTEGER亲和性。典型的如INT、INTEGER、TINYINT、BIGINT等。此列会尝试将数据解释为整数。
4. REAL亲和性:类型名包含“REAL”、“FLOA”或“DOUB”时,列亲和性为REAL。例如REAL、DOUBLE、FLOAT。该列倾向于将数据存储为浮点数。
5. NONE亲和性:如果类型名不包含以上任何关键字(或者直接没有声明类型),则亲和性为NONE。例如BLOB(因为BLOB优先级高于其他判断,但若单独写BLOB则直接判定为NONE亲和性?实际上规则稍复杂:如果包含“BLOB”会被优先赋予NONE亲和性)。具有NONE亲和性的列不会进行任何类型转换,存入什么存储类就是什么存储类。
下面用一张简单的表来验证亲和性推断:
-- 查看SQLite推断的列亲和性(通过pragma)
CREATE TABLE test_affinity (
a TEXT,
b NUMERIC,
c INT,
d REAL,
e BLOB,
f FLOAT,
g VARCHAR(50),
h BIGINT,
i DOUBLE,
j NONE
);
PRAGMA table_info(test_affinity);执行PRAGMA table_info后,type列显示的是声明的原始类型,但SQLite内部已经标记好了亲和性。可以看到g VARCHAR(50)实际上是TEXT亲和性,h BIGINT是INTEGER亲和性,j NONE是NONE亲和性。
数据插入时的转换规则
当使用INSERT或UPDATE将值存到某个列时,SQLite会尝试根据列的亲和性对传入的值进行转换,但转换不是任意的,有一定的确定性规则:
- TEXT亲和性:若传入的值是文本或BLOB,直接存储;若是整数或实数,则转换为文本形式存储。
- NUMERIC亲和性:如果值是文本,且看起来像数字(例如“15”、“3.14”),则尝试将其转换为INTEGER或REAL存储(优先整数)。若无法转换,则保持TEXT存储。对于整数或实数,直接存储。对于NULL和BLOB,不做转换。
- INTEGER亲和性:类似NUMERIC,但仅尝试转换为整数。如果文本是类似“3.0”的浮点数表示,会转换为整数3;而对于“3.14”,转换会失败并存储为TEXT。
- REAL亲和性:尝试将值转换为实数存储;若文本可被解释为数值,则转成REAL;否则保持TEXT。
- NONE亲和性:完全不转换,存入的存储类就是传入值的存储类。
下面的示例展示了INTEGER亲和性列的行为:
CREATE TABLE demo_int (id INTEGER);
INSERT INTO demo_int VALUES ('123'), ('4.56'), ('hello'), (42.9);
SELECT id, typeof(id) FROM demo_int;结果可能显示:123的存储类是integer;4.56由于无法精确转为整数(文本"4.56"转换为整数会先转成实数再截断?实际上规则是INTEGER亲和性只接受看起来像整数的文本,而"4.56"看起来像浮点数,会被转成REAL存储为4.56?不,整数亲和性下,文本"4.56"会尝试转换为整数,但转换失败后按照规则会存储为TEXT,因为一个纯数字串但包含小数点时,SQLite不会强行截断。更精确地说:如果文本看起来像整数(可选的负号+数字),则转INTEGER;如果看起来像浮点数,则可能转REAL或TEXT,具体看亲和性:INTEGER亲和性下,对非整数文本,存储为TEXT;REAL亲和性下则转为REAL。所以'4.56'在INTEGER列中会以TEXT存储,typeof返回text)。而42.9在插入时会被截断为整数42并存储为integer。
表达式中的隐式类型转换
更让开发者困惑的是在WHERE条件、ORDER BY、比较运算和算术运算中的隐式转换。SQLite为了让不同类型之间可以操作,定义了一套基于“存储类优先级”的转换顺序:
- 比较运算(=、!=、<、>等)中,如果两个操作数的存储类不同,会根据以下规则尝试将其中一个转换为与另一个兼容:
如果一个是NULL,结果总是NULL。
如果一个是整数或实数,另一个是文本或BLOB,则尝试将文本或BLOB转换为实数进行比较。如果转换失败,则文本或BLOB被认为小于任何数字。
文本与BLOB比较时,按二进制顺序比较。
特别要注意的是,LIKE、GLOB运算符要求两边都是文本,否则会报错或产生意想不到的结果。
来看一个经典的隐式转换导致的排序问题:
CREATE TABLE sort_demo (value TEXT);
INSERT INTO sort_demo VALUES ('2'), ('10'), ('1');
SELECT value FROM sort_demo ORDER BY value;
-- 结果为 1, 10, 2 (字典序)
SELECT value FROM sort_demo ORDER BY CAST(value AS INTEGER);
-- 结果为 1, 2, 10 (数值序)因为列被声明为TEXT,值是文本存储类,ORDER BY默认按文本排序得到“1, 10, 2”,这常常与预期不符。使用显式CAST可以强制数值比较。在比较表达式中,比如WHERE value > 5,SQLite会自动尝试将value转为数值再比较,所以“10”>5成立,但“2”>5也成立?不对,“2”转成数字2,不大于5。这里正是隐式转换在起作用:数值与文本比较时,文本会被转成数值。
在UNION、INTERSECT等复合查询中,不同列的亲和性可能不同,SQLite会挑选一个“最宽泛”的类型作为结果列亲和性,通常是TEXT或NUMERIC。
综合示例与最佳实践
假设我们设计一个包含混合数据的日志表:
CREATE TABLE event_log (
id INTEGER PRIMARY KEY,
event_type TEXT,
payload NUMERIC
);
INSERT INTO event_log VALUES
(1, 'click', 42),
(2, 'impress', 3.14),
(3, 'error', 'error_code_5'),
(4, 'click', '100');
SELECT * FROM event_log WHERE payload > 10;由于payload是NUMERIC亲和性,插入的字符串“error_code_5”无法转换为数值,存储为TEXT;而“100”会转为整数100存储。执行WHERE payload > 10时,文本“error_code_5”会被尝试转为实数比较,失败后按照规则文本被视为小于任何数字,因此它不会出现在结果中。整数值42、100和实数值3.14中,只有42和100大于10。结果集中包含id为1和4的行。
理解类型亲和性与隐式转换之后,有几个务实的建议:
- 尽可能使用一致的数据类型,避免混合存储:虽然SQLite允许任意存储,但混乱的类型会让查询逻辑难以预测。
- 利用列亲和性完成自动格式转换:例如将UNIX时间戳作为整数存入INTEGER列,插入字符串“1672531200”也会自动转为整数,这可以简化应用层数据清洗。
- 复杂比较时使用
CAST明确意图:尤其是涉及TEXT列的数字排序、BLOB比较时,显式转换可以消除歧义。 - 避免完全依赖隐式转换绕过显式约束:虽然可以,但代码可读性和后期维护成本会上升。
SQLite的类型系统赋予了它极大的灵活性,是它在嵌入式、移动端、测试场景中广受欢迎的原因之一。透彻掌握其规则,就能在享受轻盈的同时写出健壮的SQL语句。
SQLitetype_affinityimplicit_conversion修改时间:2026-08-12 04:16:56