SQLite模糊搜索优先用LIKE,因其默认不区分大小写、支持%和_通配符且索引友好;GLOB区分大小写、用*和?、支持字符类但无索引优化,仅适用于路径匹配等特定场景。

SQLite模糊搜索要用LIKE还是GLOB?
SQLite默认用LIKE做模糊匹配,不是=也不是正则——这点容易错。它支持%(匹配任意长度)和_(匹配单个字符),比如WHERE name LIKE 'Li%'能查到Lisa、Lin,但LIKE 'Li_'只匹配Lin、Lib这种刚好3字母的。
GLOB也可以用,但区分大小写且用*和?,更像shell通配;除非你明确需要大小写敏感或路径式匹配,否则统一用LIKE更稳妥。
-
LIKE默认不区分大小写(受PRAGMA case_sensitive_like控制,一般保持默认) - 如果字段是
TEXT类型且建表时没指定COLLATE NOCASE,仍可能因区域设置导致意外行为 - 避免在
LIKE左边加%(如'%word'),否则无法走索引,全表扫描
C++中绑定参数防止SQL注入
手拼SQL字符串(比如"SELECT * FROM user WHERE name LIKE '" + input + "%'")极其危险,用户输abc%' OR 1=1 --就完蛋。必须用预处理语句+参数绑定。
用sqlite3_prepare_v2编译语句,再用sqlite3_bind_text传值:
立即学习“C++免费学习笔记(深入)”;
const char* sql = "SELECT id, name FROM user WHERE name LIKE ?"; sqlite3_stmt* stmt; sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr); std::string pattern = input + "%"; // 前缀匹配 sqlite3_bind_text(stmt, 1, pattern.c_str(), -1, SQLITE_STATIC);
-
sqlite3_bind_text第四个参数别写SQLITE_TRANSIENT后忘了管理内存,用SQLITE_STATIC更安全(只要pattern生命周期覆盖执行期) - 如果用户输入含
%或_想当普通字符搜,得先escape:LIKE ? ESCAPE '\',然后把\%、\_替换掉原始输入 - 不要用
sqlite3_exec跑带参数的模糊查询——它不支持绑定,只能拼串,等于放弃防护
中文模糊搜索为什么总查不到?
不是C++代码问题,是SQLite默认的LIKE对UTF-8多字节字符支持有限:它按字节比,不是按Unicode字符比。比如中文“李”是3字节,_只占1字节,LIKE '_李'永远不匹配。
解决方案只有两个:
- 改用
substr()或instr()函数(SQLite 3.7.15+),例如WHERE instr(name, ?) > 0,但无法利用索引,大数据量会慢 - 建表时加
COLLATE LOCALIZED(需编译时启用ICU)或用COLLATE NOCASE配合LIKE前缀匹配(如name LIKE '李%'),这是最常用且高效的做法 - 确保C++读写数据库时,
sqlite3_open之后立刻执行PRAGMA encoding = "UTF-8"(虽然通常默认就是,但显式设更安心)
性能差?检查有没有给模糊字段建索引
LIKE只有前缀匹配(column LIKE 'abc%')能用索引;'%abc'或'%abc%'一定全表扫。别指望加索引就能救回所有模糊场景。
- 对高频模糊字段,建普通B-tree索引就行:
CREATE INDEX idx_user_name ON user(name) - 如果常做
WHERE name LIKE 'Li%',这个索引有效;但LIKE '%li%'完全无效 - 真要支持任意位置搜索,考虑FTS5虚拟表:
CREATE VIRTUAL TABLE user_fts USING fts5(name),然后用SELECT * FROM user_fts WHERE name MATCH 'li*',但要注意FTS5不支持ORDER BY直接套原表字段,得JOIN回来
模糊搜索不是万能解法,数据量上来后,优先想能不能转成前缀+精确组合,或者引入Elasticsearch这类专用引擎。SQLite的LIKE只是轻量级兜底方案。


















