SQLite虽然是个轻量级嵌入式数据库,但内置的字符串函数相当齐全。无论是清洗导入的脏数据、拆分字段,还是生成报表,SUBSTR、REPLACE、TRIM这三类函数几乎是绕不开的工具。本文结合具体SQL示例,把它们的参数规则、边界行为和常见坑一次讲清楚。

SUBSTR:按位置截取子串
SUBSTR的基本语法是SUBSTR(字符串, 起始位置, 长度)。起始位置从1开始计数,这一点和很多编程语言从0开始不同,写SQL时容易踩坑。比如SUBSTR('SQLite', 1, 3)返回"SQL",而SUBSTR('SQLite', 4)省略第三个参数时,会从第4个字符一直取到末尾,返回"ite"。
起始位置还支持负数,表示从字符串末尾往前数。例如SUBSTR('SQLite', -2)返回末尾两个字符"te"。需要特别注意的是,当长度参数为负数或0时,SQLite的行为与某些数据库不同:长度为负数时返回的结果可能不符合直觉,实际开发中建议始终传入非负长度,避免不同版本间的差异。
-- 建表并插入测试数据
CREATE TABLE users (
id INTEGER PRIMARY KEY,
phone TEXT
);
INSERT INTO users (phone) VALUES ('13812345678'), ('15987654321');
-- 截取手机号中间四位(掩码场景)
SELECT phone,
SUBSTR(phone, 1, 3) || '****' || SUBSTR(phone, 8, 4) AS masked_phone
FROM users;
-- 结果:138****5678、159****4321
-- 取文件扩展名(从末尾截取)
SELECT SUBSTR('report.pdf', -3) AS ext; -- 返回 pdf
一个典型应用是手机号脱敏和文件扩展名提取。上面示例中||是SQLite的字符串拼接运算符,配合SUBSTR可以灵活组装输出格式。对于UTF-8中文文本,SUBSTR按字符而非字节计算(前提是数据库以UTF-8编码存储),SUBSTR('数据库入门', 1, 3)会正确返回"数据库"三个汉字。
REPLACE:批量文本替换的规则与陷阱
REPLACE的语法是REPLACE(原字符串, 查找串, 替换串),它会把原字符串中所有出现的查找串全部替换掉,属于全局替换,不需要额外开关。例如REPLACE('a,b,c', ',', '-')返回"a-b-c"。
第一个容易忽略的点是大小写敏感:REPLACE按二进制比较,'A'和'a'被视为不同字符。如果想做不区分大小写的替换,可以先用LOWER或UPPER统一处理,或者改用自定义函数。第二个点是不支持通配符和正则,查找串是纯字面匹配,需要模糊替换时得借助SQLite的正则扩展或其他手段。
-- 清洗数据:统一日期格式中的斜杠为横线
UPDATE logs
SET created_at = REPLACE(created_at, '/', '-')
WHERE created_at LIKE '%/%';
-- 去掉文本中的所有空格
SELECT REPLACE('hello sqlite world', ' ', '') AS cleaned;
-- 返回:hellosqliteworld
-- 嵌套替换:处理HTML转义
SELECT REPLACE(
REPLACE('Tom <Jerry>', '<', ''),
'>', '') AS stripped;
-- 返回:Tom Jerry
性能方面要留意:REPLACE作用于全表更新时会导致整行重写,如果表中数据量大且只有少量行需要替换,务必加WHERE条件过滤,像上面示例那样先用LIKE '%/%'限定范围,能显著减少不必要的写入。另外REPLACE可以嵌套调用,多个替换串可以层层套起来一次完成。
TRIM:去除首尾字符与自定义字符集
TRIM默认去掉字符串首尾的空格,这在清洗用户输入或导入的CSV数据时非常常用。语法上TRIM(' hello ')返回"hello",且只处理两端,中间的空格不受影响。它还有两个兄弟函数LTRIM和RTRIM,分别只处理左端和右端。
更实用的能力是第二个参数——自定义要去除的字符集。TRIM('xxhelloxx', 'x')返回"hello",SQLite会从两端逐个检查字符是否在给定集合中,遇到不在集合中的字符就停止。这个特性常用来去掉带单位的数据,例如"12.5kg"末尾的"kg"。
-- 去除首尾空格
SELECT TRIM(' SQLite ') AS t1; -- SQLite
SELECT LTRIM(' SQLite') AS t2; -- SQLite
SELECT RTRIM('SQLite ') AS t3; -- SQLite
-- 去掉数据末尾的单位字符
SELECT TRIM('12.5kg', 'kg') AS weight; -- 12.5
-- 组合使用:清洗后截取
SELECT SUBSTR(TRIM(name), 1, 10) AS short_name
FROM products;
注意字符集是按单字符匹配的,TRIM('abcba', 'ab')会去掉两端的a和b,返回"cb",它不会把"ab"当成一个整体子串去移除。如果需要移除特定子串,应该用REPLACE而不是TRIM的字符集参数。
配套函数与综合实战
字符串处理很少单打独斗,几个配套函数值得一起掌握。INSTR(字符串, 子串)返回子串首次出现的位置,找不到返回0;LENGTH返回字符数;UPPER和LOWER做大小写转换。INSTR配合SUBSTR可以按分隔符拆分字段,弥补SQLite没有内置SPLIT函数的不足。
-- 按"|"拆分:取第二段
CREATE TABLE items (tag TEXT);
INSERT INTO items (tag) VALUES ('food|fruit|apple'), ('food|meat|pork');
SELECT SUBSTR(
tag,
INSTR(tag, '|') + 1, -- 第二段起点
INSTR(tag, '|', INSTR(tag, '|') + 1) -- 第二个分隔符位置
- INSTR(tag, '|') - 1 -- 第二段长度
) AS second_tag
FROM items;
-- 结果:fruit、meat
综合来看,清洗一段用户输入的完整流程通常是:先用TRIM去掉两端空白,再用REPLACE剔除非法字符或统一格式,最后用SUBSTR截取或用INSTR定位拆分。把这些函数组合成表达式写在SELECT或UPDATE里,比把数据拉到应用层再处理效率高得多,也更利于维护。建议在实际项目中多利用SELECT 表达式先验证结果,确认无误后再执行UPDATE,避免误改数据。