Schnelle Masseneinsätze in sqlite3?
Frage
Ich habe eine Datei von etwa 30000 Zeilen von Daten, die ich in eine sqlite3 Datenbank laden möchten. Gibt es einen schnelleren Weg, als für jede Zeile von Daten Einfügeanweisungen Erzeugen?
Die Daten sind durch Leerzeichen getrennte und ordnet direkt zu einem sqlite3 Tisch. Gibt es irgendeine Art von Bulk-Insert-Methode für das Hinzufügen von Volumendaten in eine Datenbank?
Hat jemand etwas hinterhältig wunderbare Art und Weise, dies zu tun entwickelt, wenn es in nicht gebaut wird?
Ich soll das Vorwort mit der Frage, gibt es eine C ++, wie es von der API zu tun?
Lösung
Sie können auch versuchen, einige Parameter zwicken aus ihm zusätzliche Geschwindigkeit zu bekommen. Insbesondere möchten Sie wahrscheinlich PRAGMA synchronous = OFF;
.
Andere Tipps
- wickelt alle Einfügungen in einer Transaktion, auch wenn es ein einzelner Benutzer, es ist viel schneller.
- verwenden vorbereitete Anweisungen.
Sie möchten den .import
Befehl verwenden. Zum Beispiel:
$ 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
Beachten Sie, dass diese Bulkbeladung Befehl nicht SQL, sondern eine benutzerdefinierte Funktion von SQLite. Als solches hat es eine seltsame Syntax, weil wir es über echo
an die interaktiven Kommandozeileninterpreter vorbei sind, sqlite3
.
In PostgreSQL ist das Äquivalent COPY FROM
:
http://www.postgresql.org/docs/8.1/static/sql -copy.html
In MySQL ist es LOAD DATA LOCAL INFILE
:
http://dev.mysql.com/doc/refman/5.1 /en/load-data.html
Eine letzte Sache: Speicher mit dem Wert von .separator
vorsichtig zu sein. Das ist ein sehr häufiges Gotcha wenn Bulk-Einsätze zu tun.
sqlite> .show .separator
echo: off
explain: off
headers: on
mode: list
nullvalue: ""
output: stdout
separator: "\t"
width:
Sie sollten explizit den Separator gesetzt, bevor Sie .import
ein Leerzeichen, Tabulator oder Komma zu sein.
-
Erhöhung
PRAGMA default_cache_size
zu einer viel größeren Zahl. Dieser Wille erhöht die Anzahl der Seiten im Cache im Speicher. -
Wickeln Sie alle Einsätze in einer einzigen Transaktion eher als eine Transaktion pro Zeile.
- Verwenden Sie kompilierten SQL-Anweisungen, die Einsätze zu tun.
- Schließlich, wie bereits erwähnt, wenn Sie bereit verzichten vollständige ACID Compliance sind, setzen
PRAGMA synchronous = OFF;
.
RE: „Gibt es einen schnelleren Weg, dass für jede Zeile von Daten Einfügeanweisungen Erzeugen“
Erstens: es Schnitt auf 2 SQL-Anweisungen nach unten von Virtuelle Tabellen-API Verwendung von Sqlite3 der Herstellung eg
create virtual table vtYourDataset using yourModule;
-- Bulk insert
insert into yourTargetTable (x, y, z)
select x, y, z from vtYourDataset;
hier Die Idee ist, dass Sie eine C-Schnittstelle implementieren, die die Quelldaten gesetzt liest und präsentieren sie als virtuelle Tabelle auf SQLite und dann tun Sie eine SQL-Kopie von der Quelle in die Zieltabelle in einem Rutsch. Es klingt schwieriger, als es wirklich ist, und ich habe große Verbesserungen in der Geschwindigkeit auf diese Weise gemessen.
Zweitens: Nutzen Sie die anderen raten hier zur Verfügung gestellten heißt die Pragma-Einstellungen und die Verwendung einer Transaktion machen
.Drittens: Vielleicht sehen, wenn Sie mit einigen der Indizes für die Zieltabelle abschaffen kann. Auf diese Weise SQLite hat weniger Indizes für jede Zeile aktualisieren eingefügt
Es gibt keinen Weg zum Masseneinsatz, aber gibt es eine Möglichkeit, große Teile zu schreiben in dem Speicher, begehen sie dann an den Datenbank. Für den C / C ++ API, nur tun:
sqlite3_exec (db, "BEGIN TRANSACTION", NULL, NULL, NULL);
... (INSERT-Anweisungen)
sqlite3_exec (db "COMMIT TRANSACTION", NULL, NULL, NULL);
db Unter der Annahme, ist die Datenbank-Zeiger.
Ein guter Kompromiss ist Ihren EINSATZ zu wickeln zwischen BEGIN; und Ende; Stichwort heißt:
BEGIN;
INSERT INTO table VALUES ();
INSERT INTO table VALUES ();
...
END;
In Abhängigkeit von der Größe der Daten und die Menge an RAM zur Verfügung, eine der besten Performance-Gewinne treten durch SQLite Setzen eine All-in-Memory-Datenbank zu verwenden, anstatt auf der Festplatte geschrieben werden.
In-Memory-Datenbanken, NULL als Dateiname Argument übergeben sqlite3_open
und stellen Sie sicher, dass TEMP_STORE definiert ist, in geeigneter Weise
(Alle obigen Text ist aus meiner eigenen Antwort auf eine separaten sqlite- exzerpiert damit verbundene Frage )
ich das eine gute Mischung für einen One-Shot langen Import zu sein gefunden.
.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
Quelle: http: //erictheturtle.blogspot. sein / 2009/05 / am schnellsten Bulk-Import-in-sqlite.html
einige zusätzliche Informationen: http: //blog.quibb .org / 2010/08 / Fast-Bulk-Einsätze-in-sqlite /
Wenn Sie nur einmal einfügen, kann ich einen schmutzigen Trick für Sie haben.
Die Idee ist einfach, zuerst in eine Memory-Datenbank einfügen, dann Backup und schließlich zu Ihrer ursprünglichen Datenbank-Datei wiederherstellen.
Ich schrieb die detaillierten Schritte unter meine Blog . :)