如何通过Shell管道将纯CSV数据导入SQLite表并管控约束违规
嘿,这个需求我刚好折腾过几次,给你整理了两种实用的方法,既能完美把程序的CSV输出导入SQLite,还能灵活控制约束违规的处理:
方法1:用SQLite内置的
.import命令(快速省心) 这种方式是SQLite官方推荐的批量导入方式,操作简单,适合大多数场景。
第一步:先搭好目标表的结构
首先得确保你的SQLite表和CSV的字段完全匹配——字段数量、顺序、数据类型都要对应上。比如假设你的CSV输出是user_id,username,email,且user_id是主键,那先创建表:
CREATE TABLE IF NOT EXISTS app_users ( user_id INTEGER PRIMARY KEY, username TEXT NOT NULL, email TEXT UNIQUE );
第二步:基础导入(违规就停止)
直接把程序的标准输出管道到sqlite3命令里,用.import读取标准输入:
./your_program | sqlite3 your_database.db -cmd ".mode csv" ".import /dev/stdin app_users"
这里的参数解释下:
.mode csv:告诉SQLite接下来的输入是纯CSV格式/dev/stdin:指定从标准输入读取内容(也就是你程序的输出)app_users:目标表的名称
默认情况下,如果遇到重复主键这类约束违规,SQLite会直接报错并停止导入。
第三步:自定义约束违规的处理
如果想跳过重复记录,或者用新记录替换旧的,咱们可以先把CSV导入临时表,再用INSERT ... ON CONFLICT来处理:
./your_program | sqlite3 your_database.db <<'EOF' -- 切换到CSV模式 .mode csv -- 把程序输出导入临时表 .import /dev/stdin temp_import_table -- 插入到目标表,这里选择跳过重复主键的记录 INSERT INTO app_users SELECT * FROM temp_import_table ON CONFLICT(user_id) DO IGNORE; -- 要是想替换旧记录,就换成下面这句: -- INSERT INTO app_users SELECT * FROM temp_import_table ON CONFLICT(user_id) DO REPLACE; -- 甚至可以自定义更新逻辑,比如只更新邮箱: -- INSERT INTO app_users SELECT * FROM temp_import_table ON CONFLICT(user_id) DO UPDATE SET email = excluded.email; -- 最后删掉临时表 DROP TABLE temp_import_table; EOF
这里的ON CONFLICT子句是核心,你可以根据需求选:
DO IGNORE:遇到违规记录直接跳过DO REPLACE:用新记录覆盖旧的(会删除旧记录再插入新的,注意关联数据的影响)DO UPDATE:自定义更新字段,灵活度最高
方法2:生成INSERT语句导入(极致灵活)
如果需要更精细的控制(比如给某些字段加默认值、做简单转换),可以把CSV转换成带冲突处理的INSERT语句,再导入SQLite:
./your_program | awk -F',' '{printf "INSERT INTO app_users (user_id, username, email) VALUES (%s, '%s', '%s') ON CONFLICT(user_id) DO IGNORE;\n", $1, $2, $3}' | sqlite3 your_database.db
因为你说CSV字段只有小写字母、数字和点号,没有特殊字符,所以直接用awk拼接是安全的,不用担心转义问题。
几个要注意的细节
- 表结构必须严格匹配:CSV的字段顺序、数量一定要和目标表一致,不然会出现数据错位或者导入失败
- 约束要提前定义:只有表中明确设置了主键、唯一约束等,
ON CONFLICT才能生效 - 大文件导入优化:如果导入的数据量很大,可以先执行
PRAGMA journal_mode=WAL;和PRAGMA synchronous=OFF;来加速,导入完成后再恢复默认设置
内容的提问来源于stack exchange,提问作者Ivan
相关产品推荐
相关产品推荐

