在 SQLite 3 里,很多人会下意识写出 INSERT IF NOT EXISTS ...,但这条语法本身并不存在。真正要做“防重插”,要么依赖表上的唯一约束配合 INSERT OR IGNORE,要么改写成带子查询的 INSERT ... SELECT ... WHERE NOT EXISTS。
这两种方案看起来都能达到“只插一次”的效果,但适用前提完全不同:前者更快,也更稳,前提是你已经定义了 UNIQUE 或 PRIMARY KEY;后者更灵活,适合按多个字段组合判断是否重复。看完下面几节,你可以直接判断当前表结构该用哪一种,以及哪些写法不能拿来替代。
SQLite 3 为什么不能直接写 INSERT IF NOT EXISTS
先说结论:SQLite 3 不支持用 IF NOT EXISTS 直接修饰 INSERT。也就是说,类似下面这种写法会直接报错:
INSERT IF NOT EXISTS INTO users (name) VALUES ('Alice');
IF NOT EXISTS 在 SQLite 里常见于 CREATE TABLE IF NOT EXISTS 这类 DDL 语句,但不能照搬到 INSERT 上。要实现“不存在才插入”,只能通过约束机制或查询判断来绕开。
方案一:用 INSERT OR IGNORE 配合唯一约束
这是最常用、通常也是性能最好的一种方式。前提只有一个:目标字段必须已经声明为 UNIQUE 或 PRIMARY KEY。
为什么它最常见
INSERT OR IGNORE遇到冲突时不会报错- 它不会更新已有行,只会静默跳过本次插入
- 对已经建好唯一约束的表来说,写法最短,执行成本也最低
先看建表示例
CREATE TABLE users (
id INTEGER PRIMARY KEY,
email TEXT UNIQUE
);
这个表里,id 是主键,email 带有唯一约束。此时就可以安全使用:
INSERT OR IGNORE INTO users (email) VALUES ('alice@example.com');
如果 email 已经存在,这条语句会被直接忽略,不抛异常,也不会影响其他操作。
使用时要注意什么
INSERT OR IGNORE 能成立,关键不在 IGNORE,而在唯一约束本身。没有约束,SQLite 根本不知道什么叫“重复”,结果就是照样插入多条相同数据。
另外,它对 NOT NULL 冲突同样会生效,但如果你的目标是“防重插”,真正决定行为的仍然是 UNIQUE 或 PRIMARY KEY。
方案二:用 INSERT ... SELECT ... WHERE NOT EXISTS
如果表上没有现成的唯一约束,或者你要按多个字段组合判断是否重复,那么可以改用 INSERT ... SELECT ... WHERE NOT EXISTS。
它适合哪些场景
这种写法不依赖唯一约束,完全靠逻辑判断是否已存在,因此更灵活。比如你希望 name 和 age 同时匹配时才视为重复,就可以这样写:
INSERT INTO users (name, age)
SELECT 'Alice', 25
WHERE NOT EXISTS (
SELECT 1
FROM users
WHERE name = 'Alice' AND age = 25
);
这里的 SELECT 1 比 SELECT * 更轻量,语义也更清楚:子查询只是在判断“是否存在至少一行”。
它的代价和限制
- 每次执行都要额外判断一次存在性,大数据量下会明显变慢
- 如果子查询条件没有索引,扫描成本会更高
- 它本质上是逻辑判断,不如约束型方案直接
- 写法更长,也更容易因为括号、条件或字段名出错
所以,这种方式更像是“没有唯一约束时的替代方案”,而不是优先方案。
为什么不要把 REPLACE INTO 当成替代品
很多人看到“冲突时处理”就会想到 REPLACE INTO,但它并不等于“存在就跳过”。它的真实语义是:冲突则删掉旧行,再插入新行。
REPLACE INTO users (id, email) VALUES (1, 'alice@example.com');
这类写法的风险在于,它可能触发两步动作:
- 先删除原有记录
- 再插入一条新记录
也正因为如此,它常常带来额外副作用,比如:
id变化,尤其是在自增主键场景下更容易踩坑- 触发
DELETE相关逻辑 - 影响外键级联、触发器或关联数据
REPLACE INTO 实际上等价于 INSERT OR REPLACE。除非你明确需要“存在即覆盖”的语义,否则不要把它当作 INSERT IF NOT EXISTS 的替身。
常见错误和几个容易忽略的坑
1. 忘记建 UNIQUE 约束
这是最常见的问题。很多人直接上 INSERT OR IGNORE,却没有给目标字段加 UNIQUE,结果就是语句看起来没报错,但重复数据照样进表。
2. 子查询语法不完整
WHERE NOT EXISTS 写法里,漏掉 FROM 表名、括号不匹配,或者条件拼接不完整,都可能得到类似这样的报错:
near "WHERE": syntax error
3. Python 的 sqlite3 参数绑定容易写乱
用 Python 的 sqlite3 执行这类带子查询的 INSERT 时,参数往往不能随手在主句和子查询之间混着绑定。实际开发里,通常要么把 SQL 先完整拼好,要么拆成两步执行,避免生成不合法语句。
4. UNIQUE 对 NULL 不按重复处理
SQLite 的 INSERT OR IGNORE 对 PRIMARY KEY 冲突有效,但在 UNIQUE 约束字段上,多个 NULL 并不视为重复值。这意味着如果你的去重逻辑依赖该列,而该列允许 NULL,结果可能和预期不一致。
该选哪一种写法
如果你的表结构已经有 UNIQUE 或 PRIMARY KEY,优先选 INSERT OR IGNORE。它写法简单,执行效率也更高,适合绝大多数“避免重复插入”的需求。
如果你需要按多个字段组合判断,或者暂时无法修改表结构,再考虑 INSERT ... SELECT ... WHERE NOT EXISTS。它更灵活,但执行成本和出错概率也更高。
最后再强调一次:REPLACE INTO 不是“只在不存在时插入”,而是“冲突时删除旧行再插入新行”。这两种语义差别很大,不能混用。









