CSV 文件是数据交换中最常见的轻量格式,而 SQLite 也常被用作本地数据处理与原型验证的存储引擎。sqlite3 命令行工具提供的 .import 命令,可以在不编写 Python、Java 等脚本的情况下,把 CSV 文件内容直接导入指定表。它属于 sqlite3 外壳的点命令而非 SQL 语句,执行逻辑与 INSERT 批量写入类似,但省去了手工拆分字段和拼接 SQL 的过程。

实际使用 .import 时,解析方式、表结构、表头行、空值处理等因素都会影响导入结果。本文围绕这些细节展开,帮助读者在不同场景下正确高效地完成 CSV 导入。
一、.import 命令的基本语法与工作方式
在 sqlite3 交互式终端中,.import 的标准语法是:
.import 文件名 表名
例如把 users.csv 导入到 users 表,需要先打开数据库,然后设置 CSV 模式,最后执行导入:
sqlite3 app.db .mode csv .import users.csv users
这里 .mode csv 非常关键。它告诉 sqlite3 外壳在导入时按照 CSV 规范解析文件,包括正确处理被双引号包裹的字段、字段内部的逗号以及换行。如果省略这一步,默认的分隔符是竖线,CSV 文件中的逗号不会被识别为分隔符,整行数据可能被当作单个字段写入第一列。
从执行流程看,.import 由 sqlite3 外壳读取文件内容,逐行解析后将数据写入 SQLite 表。若目标表已经存在,则按行追加数据;若目标表不存在,外壳会先根据文件首行自动建表,再插入剩余行。这个行为在不同版本中略有差异,但核心逻辑一致。正因如此,使用前需要明确目标表是否存在,以及 CSV 首行是否为表头。
二、导入已有表:模式设置、分隔符与表头处理
当目标表已经通过 CREATE TABLE 定义好时,.import 只负责追加数据,不会修改表结构。此时必须保证 CSV 文件的列顺序与表定义的列顺序完全一致。例如已有表结构如下:
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL,
created_at TEXT
);
对应的 CSV 文件应该按 id,name,email,created_at 的顺序组织。如果第一行就是数据,不包含列名,可以直接导入:
.mode csv .import users.csv users
但如果 CSV 文件首行是列名,就要避免把列名也写入表。SQLite 3.32 及以上版本支持 .import 的 --skip 参数,可以跳过前 N 行。下面命令会跳过首行表头后导入:
.import --csv --skip 1 users.csv users
这里 --csv 与 .mode csv 作用等价,但写在 .import 参数中更适合一行命令完成。对于旧版本 sqlite3,可以先创建临时表导入,再用 INSERT INTO ... SELECT 剔除表头,或者用 .headers on 配合查询过滤,但都比较繁琐。
除了逗号分隔的 CSV,.import 也能处理制表符分隔的文件。此时使用 .mode tabs 即可。需要注意在 shell 中输入制表符时不要与命令补全冲突。
三、自动建表的规则与类型问题
如果执行 .import 时目标表不存在,sqlite3 外壳会根据 CSV 首行创建表,并把首行内容作为列名。例如 users.csv 首行是 id,name,email,执行下面命令后,SQLite 会生成一个包含三列的新表:
.mode csv .import users.csv users .schema users
查看结构可以看到:
CREATE TABLE users( "id" TEXT, "name" TEXT, "email" TEXT );
自动建表时所有列都被定义为 TEXT 类型。SQLite 本身对类型约束较为宽松,普通文本查询通常不受影响,但在数值比较、排序、日期计算等场景下可能出现意外行为。例如 id 列会被当作字符串,数值 10 与 9 的排序结果可能不符合预期。更推荐的做法是先手动定义精确的表结构,再导入数据,这样能控制主键、默认值、外键和数据类型。
如果已经自动建表且需要调整类型,可以导出数据后重建表,或者使用 SQL 语句将列值转换。例如将 id 转为整数,可以创建新表并插入转换结果:
CREATE TABLE users_new (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL
);
INSERT INTO users_new (id, name, email)
SELECT CAST(id AS INTEGER), name, email
FROM users;
DROP TABLE users;
ALTER TABLE users_new RENAME TO users;
这种方式适用于数据量不大的一次性修正。对于大文件,建议第一次导入前就完成建表,避免二次转换带来的时间与存储开销。
四、常见报错与批量导入实践
使用 .import 最常见的报错是表已存在但列数不匹配。例如 CSV 一行有 5 个字段,而表只有 4 列,sqlite3 会提示类似 expected 4 columns but found 5 的错误。解决方法是检查 CSV 是否包含多余逗号、引号是否正确闭合,或者调整表结构。另一个高频问题是路径含空格,此时需要给文件名加引号,这是 sqlite3 外壳参数解析的要求。
.import "C:/data/my csv files/users.csv" users
空值处理也容易造成困惑。CSV 中的空字段导入后默认是空字符串,而不是 SQL 的 NULL。如果业务上需要区分空字符串与 NULL,可以在导入后执行更新:
UPDATE users SET email = NULL WHERE email = '';
批量导入多个 CSV 文件时,可以借助 shell 脚本循环调用 sqlite3。下面的 bash 示例将目录下所有 CSV 文件导入 SQLite,每个文件名作为表名:
for f in data/*.csv; do table=$(basename "$f" .csv) sqlite3 app.db ".mode csv" ".import $f $table" done
如果要导入的数据量很大,可以在导入前关闭同步并设置事务模式,提升写入速度:
PRAGMA journal_mode=OFF; PRAGMA synchronous=OFF; BEGIN; .import large.csv large_table COMMIT;
不过 .import 本身已经在一个事务中执行,手动开启事务并非必需,但在多个导入操作之间共用事务可以减少磁盘同步次数。数据导入完成后,建议执行 ANALYZE 更新统计信息,便于后续查询优化。
总体而言,.import 适合作为轻量级数据导入方案。对于需要清洗、类型转换或复杂校验的任务,可以先用 .import 把原始 CSV 落到临时表,再通过 SQL 转换为最终结构;也可以使用 Python 的 sqlite3 模块实现更精细的控制。理解 .import 的工作方式,能帮助开发者在数据准备阶段省下大量重复代码。
SQLite导入CSV.import命令CSV文件导入表修改时间:2026-08-30 07:19:53