DEBUG.WIKI
丽萨主机;为跨境业务选好 IP;原生 / 住宅 IP · 按套餐选择;查看 IP →
阿里云;从建站到云上业务;云服务器 · 数据库 · 对象存储;查看产品 →
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

SQLite INSERT SELECT 的 ON CONFLICT 报 near do:给 SELECT 保留 WHERE

SQLite INSERT SELECT 接 ON CONFLICT 报 near do 时,检查 ON 的解析歧义,在 SELECT 中保留 WHERE,并核对实际运行库和冲突语义。

SQLite 通过 INSERT SELECT 导入数据并附带 ON CONFLICT,执行时却报 near "do": syntax error。同一条语句去掉 FROM 或增加 WHERE 后能解析时,应检查 SELECT 与 UPSERT 之间的 ON 解析歧义,而不是先怀疑 DO NOTHING 不受支持。

当前 SQLite 文档仍要求:INSERT 的数据来自 SELECT 且后面带 UPSERT 时,SELECT 应包含 WHERE,即使条件只是 WHERE true。SQLite 的解析器可能把 ON 当成连接约束的开头,后面的错误位置因而落在 DO。

将 WHERE 放在取数据的 SELECT 中

假设普通表 target_items 和 source_items 都有 item_id、item_name 两列,target_items.item_id 具有适合业务的唯一约束。以下是只展示导入结构的写入示例:

INSERT INTO target_items (item_id, item_name)
SELECT item_id, item_name
FROM source_items
WHERE true
ON CONFLICT (item_id) DO NOTHING;

WHERE true 为 SELECT 明确了语法边界,不过滤源数据。它必须放在 ON CONFLICT 前面的 SELECT 中。DO UPDATE 后也可以有 WHERE,但后者控制是否更新冲突行,不能代替这里的 SELECT 条件。

源查询原本已有 WHERE 时,保留真实筛选条件,不要为套示例把它替换成 true。涉及租户、批次或权限范围的条件尤其不能删除。原查询还有 JOIN、排序或其他子句时,按完整 SELECT 的结构放置条件,不把 WHERE 随手插入任意子查询就期待外层解析自动恢复。

没有 FROM 的极简常量 SELECT 可能不会暴露相同症状;不能因此认为所有 INSERT SELECT 写法都可靠。生成 SQL 的 ORM 或导入工具应核对它最终发给 SQLite 的语句,而不只看代码中的查询构造器。

先确认当前连接真正使用的 SQLite 版本

在应用实际连接中查看只读信息:

SELECT sqlite_version();

JDBC、Python 或其他驱动可能携带与系统命令行不同的 SQLite 库。更新机器上的 sqlite3 命令,不保证应用驱动也已更新。

UPSERT 从 SQLite 3.24.0 开始提供;多个 ON CONFLICT 子句,以及最后 DO UPDATE 可以省略冲突目标等扩展,属于 3.35.0 起的规则。遇到较旧运行库时,按实际语法支持范围调整或安排受支持的驱动升级,不能只加 WHERE 就绕过所有版本限制。

这篇处理的是解析边界。若后续报冲突目标不匹配唯一约束、NOT NULL、CHECK 或外键错误,应按新错误检查表结构和数据。UPSERT 主要处理唯一性冲突,不会自动忽略所有约束失败,也不能让一个普通非唯一字段成为冲突键。

不要为语法错误改变冲突处理含义

DO NOTHING 会跳过相应唯一性冲突;DO UPDATE 会按已有更新表达式处理那一行。给 SELECT 增加 WHERE true 不要求把两者互相替换。原业务需要更新时,保留真实更新字段与 excluded 的使用方式。

同样,改成 INSERT OR REPLACE 不是等价的语法修复。替换式写入具有不同的数据影响,应按其删除、插入及关联行为单独评估,不能用来掩盖这个解析问题。

正式执行前保存 SQL、筛选规则和目标数据所需备份,先预览完全相同的源 SELECT。导入会新增或更新数据,DO NOTHING 也可能留下其他成功插入的行,不能把它当作无写入的试验。

解析通过后,确认处理的是正确数据

在隔离数据中覆盖无冲突、已有同键记录和需要更新的分支。检查插入或跳过数量、目标字段值,以及原有筛选范围是否保留。仅看到 syntax error 消失,不证明导入语义正确。

批次可能由多条语句或多个事务组成,失败后重跑前应确认哪些写入已提交。恢复旧 SQL 不会撤销新插入或覆盖的数据;若变更选错了导入范围,先暂停后续批次,再依据保存的输入和备份纠正。