Быстрее массовых вставок в sqlite3? - PullRequest
38 голосов
/ 12 декабря 2008

У меня есть файл с 30000 строками данных, которые я хочу загрузить в базу данных sqlite3. Есть ли более быстрый способ генерации операторов вставки для каждой строки данных?

Данные разделены пробелами и отображаются непосредственно в таблицу sqlite3. Существует ли какой-либо метод массовой вставки для добавления данных тома в базу данных?

Кто-нибудь придумал какой-нибудь изумительно прекрасный способ сделать это, если он не встроен?

Мне следует предвосхитить это, спросив, есть ли C ++ способ сделать это из API?

Ответы [ 10 ]

55 голосов
/ 12 декабря 2008
  • Обернуть все INSERT в транзакции, даже если есть один пользователь, это намного быстрее.
  • использовать подготовленные заявления.
34 голосов
/ 17 апреля 2009

Вы хотите использовать команду .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 FROM: http://www.postgresql.org/docs/8.1/static/sql-copy.html

В MySQL это LOAD DATA LOCAL INFILE: http://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.

19 голосов
/ 13 декабря 2008

Вы также можете попробовать настроить несколько параметров , чтобы получить дополнительную скорость. В частности, вы, вероятно, хотите PRAGMA synchronous = OFF;.

18 голосов
/ 13 декабря 2008
  • Увеличение PRAGMA default_cache_size в гораздо большее число. Это будет увеличить количество кэшированных страниц в памяти.

  • Обернуть все вставки в одну транзакцию вместо одной транзакции в строке.

  • Используйте скомпилированные операторы SQL для вставки.
  • Наконец, как уже упоминалось, если вы хотите отказаться от полного соответствия ACID, установите PRAGMA synchronous = OFF;.
9 голосов
/ 17 июня 2010

RE: «Есть ли более быстрый способ создания операторов вставки для каждой строки данных?»

Во-первых: сократите его до 2 операторов SQL, используя API виртуальной таблицы Sqlite3 , например.

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

Идея заключается в том, что вы реализуете интерфейс C, который читает ваш исходный набор данных и представляет его в SQlite в виде виртуальной таблицы, а затем вы за один раз делаете копию SQL из исходной в целевую таблицу. Звучит сложнее, чем есть на самом деле, и я измерил огромные улучшения скорости таким образом.

Второе: воспользуйтесь другим советом, представленным здесь, то есть настройками прагмы и использованием транзакции.

В-третьих: возможно, посмотрите, сможете ли вы покончить с некоторыми индексами в целевой таблице. Таким образом, у sqlite будет меньше индексов для обновления для каждой вставленной строки

5 голосов
/ 20 января 2010

Нет способа массовой вставки, но есть способ написать большие куски в память, а затем передать их база данных. Для API C / C ++ просто выполните:

sqlite3_exec (дБ, «НАЧАЛО СДЕЛКИ», NULL, NULL, NULL);

... (операторы INSERT)

sqlite3_exec (дБ, «СОВЕРШЕНИЕ СООТВЕТСТВИЯ», ​​НУЛЬ, НУЛЬ, НУЛЬ);

Предполагая, что db является указателем вашей базы данных.

3 голосов
/ 08 сентября 2011

Хороший компромисс - обернуть ваши ВСТАВКИ между НАЧАЛАМИ; и конец; ключевое слово т.е.:

BEGIN;
INSERT INTO table VALUES ();
INSERT INTO table VALUES ();
...
END;
2 голосов
/ 16 декабря 2008

В зависимости от размера данных и объема доступной оперативной памяти один из лучших приростов производительности будет достигнут при настройке sqlite для использования базы данных «все в памяти» вместо записи на диск.

Для баз данных в памяти, передайте NULL в качестве аргумента имени файла sqlite3_open и , убедитесь, что TEMP_STORE определено соответствующим образом

(Весь приведенный выше текст взят из моего собственного ответа на отдельный вопрос, связанный с sqlite )

1 голос
/ 18 ноября 2015

Мне показалось, что это хороший микс для импорта одним выстрелом.

.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/

0 голосов
/ 28 декабря 2009

Если вы просто вставляете один раз, у меня для вас может быть подвох.

Идея проста: сначала вставить в базу данных памяти, затем сделать резервную копию и, наконец, восстановить в исходный файл базы данных.

Я написал подробные инструкции в моем блоге . :)

...