55

我有一个包含大约 30000 行数据的文件,我想将它们加载到 sqlite3 数据库中。有没有比为每行数据生成插入语句更快的方法?

数据以空格分隔并直接映射到 sqlite3 表。是否有任何类型的批量插入方法可以将卷数据添加到数据库?

如果不是内置的,有没有人设计了一些非常棒的方法来做到这一点?

我应该先问一下,是否有 C++ 方法可以从 API 中做到这一点?

4

12 回答 12

61
  • 将所有 INSERT 包装在一个事务中,即使只有一个用户,它也快得多。
  • 使用准备好的语句。
于 2008-12-12T20:54:50.207 回答
43

您想使用该.import命令。例如:

$ cat demotab.txt
44      92
35      94
43      94
195     49
66      28
135     93
135     91
67      84
135     94

$ echo "create table mytable (col1 int, col2 int);" | sqlite3 foo.sqlite
$ echo ".import demotab.txt mytable"  | sqlite3 foo.sqlite

$ sqlite3 foo.sqlite
-- Loading resources from /Users/ramanujan/.sqliterc
SQLite version 3.6.6.2
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> select * from mytable;
col1    col2
44      92
35      94
43      94
195     49
66      28
135     93
135     91
67      84
135     94

请注意,此批量加载命令不是 SQL,而是 SQLite 的自定义功能。因此它有一个奇怪的语法,因为我们将它传递echo给交互式命令行解释器,sqlite3.

在 PostgreSQL 中相当于COPY FROMhttp ://www.postgresql.org/docs/8.1/static/sql-copy.html

在 MySQL 中是LOAD DATA LOCAL INFILEhttp ://dev.mysql.com/doc/refman/5.1/en/load-data.html

最后一件事:记住要小心.separator. 在进行批量插入时,这是一个非常常见的问题。

sqlite> .show .separator
     echo: off
  explain: off
  headers: on
     mode: list
nullvalue: ""
   output: stdout
separator: "\t"
    width:

您应该在执行之前将分隔符明确设置为空格、制表符或逗号.import

于 2009-04-17T10:15:44.460 回答
23

您还可以尝试调整一些参数以从中获得额外的速度。具体来说,您可能想要PRAGMA synchronous = OFF;.

于 2008-12-12T21:11:12.670 回答
21
  • 增加到PRAGMA cache_size 一个更大的数字。这将增加缓存在内存中的页数。注意:cache_size是每个连接的设置。

  • 将所有插入包装到一个事务中,而不是每行一个事务。

  • 使用已编译的 SQL 语句进行插入。
  • 最后,如前所述,如果您愿意放弃完全的 ACID 合规性,请设置PRAGMA synchronous = OFF;.
于 2008-12-13T00:25:00.220 回答
21

我在这里测试了答案中提出的一些编译指示:

  • synchronous = OFF
  • journal_mode = WAL
  • journal_mode = OFF
  • locking_mode = EXCLUSIVE
  • synchronous = OFF+ locking_mode = EXCLUSIVE+journal_mode = OFF

这是我在事务中插入不同数量的数字:

增加批量大小可以给你带来真正的性能提升,而关闭日志、同步、获取独占锁只会带来微不足道的收益。大约 110k 的点显示了随机后台负载如何影响您的数据库性能。

此外,值得一提的是,这journal_mode=WAL是一个很好的替代默认值。它提供了一些收益,但不会降低可靠性。

C#代码。

于 2019-10-24T18:43:38.417 回答
13

RE:“有没有更快的方法为每行数据生成插入语句?”

首先:通过使用 Sqlite3 的虚拟表 API将其减少到 2 个 SQL 语句,例如

create virtual table vtYourDataset using yourModule;
-- Bulk insert
insert into yourTargetTable (x, y, z)
select x, y, z from vtYourDataset;

这里的想法是您实现一个 C 接口,该接口读取您的源数据集并将其作为虚拟表呈现给 SQlite,然后您一次性将 SQL 复制从源表复制到目标表。这听起来比实际上更难,我已经通过这种方式测量了巨大的速度改进。

第二:利用此处提供的其他建议,即编译指示设置和使用事务。

第三:也许看看你是否可以取消目标表上的一些索引。这样,sqlite 将为插入的每一行更新更少的索引

于 2010-06-17T08:18:05.767 回答
6

没有办法批量插入,但是有办法将大块写入内存,然后将它们提交到数据库。对于 C/C++ API,只需执行以下操作:

sqlite3_exec(db, "开始交易", NULL, NULL, NULL);

...(插入语句)

sqlite3_exec(db, "提交事务", NULL, NULL, NULL);

假设 db 是您的数据库指针。

于 2010-01-20T19:21:53.110 回答
4

一个很好的折衷办法是在 BEGIN 之间包装您的 INSERTS;和结束;关键字即:

BEGIN;
INSERT INTO table VALUES ();
INSERT INTO table VALUES ();
...
END;
于 2011-09-08T09:53:46.943 回答
3

根据数据的大小和可用的 RAM 量,将 sqlite 设置为使用全内存数据库而不是写入磁盘将获得最佳性能提升之一。

对于内存数据库,将 NULL 作为文件名参数传递给sqlite3_open确保正确定义了 TEMP_STORE

(以上所有文字均摘自我自己对一个单独的 sqlite 相关问题的回答

于 2008-12-16T00:34:19.277 回答
3

我发现这对于一次性长时间导入来说是一个很好的组合。

.echo ON

.read create_table_without_pk.sql

PRAGMA cache_size = 400000; PRAGMA synchronous = OFF; PRAGMA journal_mode = OFF; PRAGMA locking_mode = EXCLUSIVE; PRAGMA count_changes = OFF; PRAGMA temp_store = MEMORY; PRAGMA auto_vacuum = NONE;

.separator "\t" .import a_tab_seprated_table.txt mytable

BEGIN; .read add_indexes.sql COMMIT;

.exit

来源:http ://erictheturtle.blogspot.be/2009/05/fastest-bulk-import-into-sqlite.html

一些附加信息:http ://blog.quibb.org/2010/08/fast-bulk-inserts-into-sqlite/

于 2015-11-18T09:16:51.110 回答
1

如果你只是插入一次,我可能会对你有一个肮脏的把戏。

这个想法很简单,首先插入内存数据库,然后备份,最后恢复到原始数据库文件。

我在我的博客上写了详细的步骤。:)

于 2009-12-28T15:03:17.413 回答
0

我用这种方法进行批量插入:

colnames = ['col1', 'col2', 'col3'] 
nrcols = len(colnames) 
qmarks = ",".join(["?" for i in range(nrcols)]) 
stmt = "INSERT INTO tablename VALUES(" + qmarks + ")" 
vals = [[val11, val12, val13], [val21, val22, val23], ..., [valn1, valn2, valn3]] 
conn.executemany(stmt, vals)

colnames must be in the order of the column names in the table 
vals is a list of db rows
each row must have the same length, and
contain the values in the correct order 
Note that we use executemany, not execute
于 2020-08-23T18:35:12.973 回答