SQLite作为一款轻量级嵌入式数据库,采用了非常灵活的动态类型系统。同一张表、同一列,理论上可以同时存储整数、文本、浮点数甚至二进制数据。这种灵活性带来了便利,但也埋下了隐患:当不同类型的值参与运算或比较时,SQLite会按照自己的内部规则做隐式转换,结果常常和直觉不符。比如把字符串'123'和整数相加没问题,但把'abc'和整数相加就会得到0,而两个看似相同的值在比较时可能返回完全不同的结果。为了让SQL语句的行为可预期,SQLite提供了CAST表达式,让开发者可以显式地把一个值转换成指定的类型。本文将从SQLite的类型亲和性讲起,结合大量可直接运行的SQL示例,详细讲解CAST的语法、转换规则和典型应用场景。

先搞懂SQLite的类型亲和性,才知道为什么需要CAST
与传统数据库严格限定列类型不同,SQLite允许向任何列插入任何类型的值。建表时声明的类型(如VARCHAR(50)、INT、TEXT)不会强制约束存储内容,而是被映射为五种类型亲和性(Type Affinity):TEXT、INTEGER、REAL、NUMERIC和BLOB。亲和性只影响值的存储形式,不做严格校验。例如往一个INTEGER亲和性的列里插入字符串'hello',SQLite照单全收,只是按TEXT方式存储。
隐式转换的规则大致是:数值和文本比较时,如果文本看起来像数字,会把文本转成数值再比较;算术运算中非数字文本会被当作0处理。这就导致了一些诡异现象。看下面的例子:
-- 创建测试表并插入混合类型数据
CREATE TABLE t1 (col INTEGER);
INSERT INTO t1 VALUES (10), ('20'), ('abc'), (NULL);
SELECT col, typeof(col), col + 1 FROM t1;
-- 结果:
-- 10 integer 11
-- 20 text 21 (文本'20'被隐式转为数值参与运算)
-- abc text 1 (非数字文本被当作0)
-- NULL null NULL注意第三行,'abc'加1的结果是1,因为'abc'被隐式当成了0。如果不加注意,这种静默的容错会在统计汇总时产生难以察觉的错误。CAST的价值就在这里:它把转换意图明确写出来,转换结果有确定规则可循,出问题时也更容易排查。
CAST的基本语法与四类目标的转换规则
CAST的语法遵循SQL标准:CAST(表达式 AS 目标类型)。在SQLite中,目标类型可以是INTEGER、REAL、TEXT、BLOB或NUMERIC。转换时保留原始值不变,返回一个新值。理解转换规则是正确使用CAST的关键,下面逐一说明。
转换为INTEGER时,如果原值是浮点数,小数部分直接舍弃(不是四舍五入);如果原值是文本,SQLite会解析开头的数字部分,遇到非数字字符就停止,开头的空格会被忽略。转换为REAL与之类似,只是结果带小数。转换为TEXT时,数值会按最可读的形式变成字符串,整数不带小数点,浮点数最多保留15位有效数字。来看一组对照示例:
SELECT
CAST(3.7 AS INTEGER), -- 结果:3,直接截断小数
CAST(-3.7 AS INTEGER), -- 结果:-3,向零取整
CAST('123abc' AS INTEGER), -- 结果:123,解析到非数字处停止
CAST('abc123' AS INTEGER), -- 结果:0,开头不是数字
CAST(' 45' AS INTEGER), -- 结果:45,忽略前导空格
CAST(123 AS TEXT), -- 结果:'123'
CAST(3.0 AS TEXT), -- 结果:'3.0'
CAST(NULL AS INTEGER); -- 结果:NULL,NULL转换后仍是NULL有两点特别容易踩坑。第一,CAST('123abc' AS INTEGER)不报错而是返回123,这和很多语言中“转换失败抛异常”的行为完全不同,如果你以为转换失败会得到NULL或报错,统计结果就会悄悄出错。第二,NULL经过任何CAST后仍然是NULL,不能指望用CAST把NULL变成0,需要这类效果应该用IFNULL(x, 0)或COALESCE函数配合。
转换为BLOB的情况稍微特殊:数值或文本转BLOB后,值按底层字节序列存储,常用在生成十六进制摘要或处理编码的场景。反过来,BLOB转TEXT时字节按UTF-8解释,如果BLOB里存的不是合法UTF-8序列,显示出来就是乱码,这一点在处理二进制字段时要多加留意。
CAST在实战中的典型应用场景
场景一:拼接数字时避免出现多余的小数点
用||拼接字符串时,如果操作数是浮点数,SQLite会把它按浮点形式转成文本,即使值是整数也可能带出".0"。例如3.0和'分'拼接会得到'3.0分',而业务上通常希望得到'3分'。这时先CAST成INTEGER就能解决:
SELECT 3.0 || '分', CAST(3.0 AS INTEGER) || '分'; -- 结果:3.0分 | 3分
场景二:清洗从外部导入的脏数据
从Excel或CSV导入数据时,金额、数量字段里经常混着'1,200'、'¥99'这类带符号的字符串,直接CAST只能解析出开头部分。稳妥的做法是先用REPLACE清洗,再转换:
CREATE TABLE orders (amount TEXT);
INSERT INTO orders VALUES ('1,200'), ('¥99'), ('- 35');
SELECT CAST(REPLACE(REPLACE(amount, ',', ''), '¥', '') AS REAL) AS clean_amount
FROM orders;
-- 结果:1200.0 / 99.0 / -35.0第三条数据'- 35'比较有意思,负号后跟空格仍能被正确解析,这是SQLite数值解析规则允许的写法。但如果字符串是'35 -'这种尾部带符号的形式,转换结果就只剩0了,所以清洗规则要根据实际数据形态定制,不能想当然。
场景三:利用转换实现取整和格式化
CAST(v AS INTEGER)本身就是一种向零取整的手段,配合ABS可以实现向下取整和向上取整:
-- 对正数向下取整、负数向上取整:直接CAST SELECT CAST(7.9 AS INTEGER); -- 7 SELECT CAST(-7.9 AS INTEGER); -- -7 -- 模拟真正的向上取整(正数场景) SELECT CAST(7.1 + 0.5 AS INTEGER); -- 7(注意这不是严格的ceil) -- 严格ceil用内置函数 SELECT CEIL(7.1); -- 8.0
可以看出CAST截断和CEIL、FLOOR这些函数语义并不相同:CAST是向零截断,FLOOR是向负无穷取整,两者对负数的处理结果不一样。需要精确取整语义时应使用内置数学函数,CAST更适合“把解析出的数字部分变成整数”这类转换用途。
场景四:日期字符串参与日期函数运算
SQLite的日期函数如strftime要求传入规范的日期文本。如果日期以整数时间戳形式存储,可以先转成TEXT再交给日期函数,或者反过来从日期文本中提取数值做计算:
-- 时间戳转可读日期
SELECT strftime('%Y-%m-%d %H:%M:%S', CAST(1700000000 AS TEXT), 'unixepoch');
-- 结果:2023-11-14 22:13:20
-- 从日期文本中提取年份数值
SELECT CAST(strftime('%Y', '2024-06-15') AS INTEGER) AS year;
-- 结果:2024,得到的是真正可参与算术运算的整数CAST与隐式转换、其他函数的对比与选择建议
不少初学者容易把CAST和typeof、CONCAT混淆。typeof(x)只是返回值的存储类型标签,不做任何转换;CAST才真正改变值的类型表现形式。另外SQLite没有独立的CONVERT函数,所有显式类型转换统一通过CAST完成,这一点和SQL Server、MySQL的函数风格不同。
在决定是否写CAST时,可以参考以下原则:当表达式涉及不同类型值之间的比较或算术运算时,显式写出CAST能让意图清晰;当拼接数值到字符串时,按需CAST成INTEGER去除多余的.0;处理外部来源的文本型数字时,先清洗再CAST并检查转换结果是否合理(比如转换后为0的记录可能是解析失败);对NULL做转换要记得结果仍是NULL,需要配合COALESCE兜底。下面用一个综合示例收尾:
SELECT
product,
IFNULL(CAST(NULLIF(price_text, '') AS REAL), 0) AS safe_price,
typeof(price_text) AS raw_type
FROM products;
-- NULLIF先排除空字符串,CAST转换数字文本,
-- 转不出来的得到NULL,最后IFNULL把NULL兜底为0掌握CAST之后,再回头看SQLite的动态类型系统就不会觉得混乱:隐式转换是数据库自作主张的猜测,显式转换是开发者给出的明确指令。养成在关键运算处主动使用CAST的习惯,SQL的执行结果会稳定得多,排查数据问题时的思路也会清晰不少。
SQLite CAST类型转换SQLite教程修改时间:2026-09-04 17:16:58