J'ai un fichier d'environ 30000 lignes de données que je souhaite charger dans une base de données sqlite3. Existe-t-il un moyen plus rapide que de générer des instructions d'insertion pour chaque ligne de données?
Les données sont délimitées par des espaces et mappées directement sur une table sqlite3. Existe-t-il une sorte de méthode d'insertion en masse pour ajouter des données de volume à une base de données?
Quelqu'un a-t-il imaginé une manière incroyablement merveilleuse de le faire s'il n'est pas intégré?
Je devrais préfacer cela en demandant s'il existe un moyen C++ de le faire à partir de l'API?
Vous pouvez également essayer peaufiner quelques paramètres pour obtenir une vitesse supplémentaire. Plus précisément, vous voulez probablement PRAGMA synchronous = OFF;
.
Vous souhaitez utiliser le .import
commande. Par exemple:
$ 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
Notez que cette commande de chargement en masse n'est pas SQL mais plutôt une fonctionnalité personnalisée de SQLite. En tant que tel, il a une syntaxe étrange car nous la transmettons via echo
à l'interpréteur de ligne de commande interactif, sqlite3
.
Dans PostgreSQL, l'équivalent est COPY FROM
: http://www.postgresql.org/docs/8.1/static/sql-copy.html
Dans MySQL, c'est LOAD DATA LOCAL INFILE
: http://dev.mysql.com/doc/refman/5.1/en/load-data.html
Une dernière chose: n'oubliez pas de faire attention à la valeur de .separator
. C'est un piège très courant lors de l'insertion en vrac.
sqlite> .show .separator
echo: off
explain: off
headers: on
mode: list
nullvalue: ""
output: stdout
separator: "\t"
width:
Vous devez explicitement définir le séparateur comme un espace, une tabulation ou une virgule avant de faire .import
.
Augmenter PRAGMA default_cache_size
à un nombre beaucoup plus élevé. Cela augmentera le nombre de pages mises en cache dans la mémoire.
Enveloppez toutes les insertions dans une seule transaction plutôt qu'une seule transaction par ligne.
PRAGMA synchronous = OFF;
.RE: "Existe-t-il un moyen plus rapide de générer des instructions d'insertion pour chaque ligne de données?"
Tout d'abord: réduisez-le à 2 instructions SQL en utilisant l'API de table virtuelle de Sqlite3 , par exemple.
create virtual table vtYourDataset using yourModule;
-- Bulk insert
insert into yourTargetTable (x, y, z)
select x, y, z from vtYourDataset;
L'idée ici est que vous implémentez une interface C qui lit votre ensemble de données source et le présentez à SQlite en tant que table virtuelle, puis vous effectuez une copie SQL de la source vers la table cible en une seule fois. Cela semble plus difficile qu'il ne l'est vraiment et j'ai mesuré d'énormes améliorations de vitesse de cette façon.
Deuxièmement: utilisez les autres conseils fournis ici, c'est-à-dire les paramètres du pragma et l'utilisation d'une transaction.
Troisièmement: voyez peut-être si vous pouvez supprimer certains des index de la table cible. De cette façon, sqlite aura moins d'index à mettre à jour pour chaque ligne insérée
Il n'y a aucun moyen d'insérer en bloc, mais il existe un moyen d'écrire de gros morceaux dans la mémoire, puis de les valider dans la base de données. Pour l'API C/C++, faites simplement:
sqlite3_exec (db, "COMMENCER LA TRANSACTION", NULL, NULL, NULL);
... (INSÉRER les instructions)
sqlite3_exec (db, "COMMIT TRANSACTION", NULL, NULL, NULL);
En supposant que db est le pointeur de votre base de données.
Un bon compromis consiste à envelopper vos INSERTS entre BEGIN; et fin; mot-clé c'est-à-dire:
BEGIN;
INSERT INTO table VALUES ();
INSERT INTO table VALUES ();
...
END;
Selon la taille des données et la quantité de RAM disponible, l'un des meilleurs gains de performances se produira en configurant sqlite pour utiliser une base de données tout en mémoire plutôt que d'écrire sur le disque.
Pour les bases de données en mémoire, passez NULL comme argument de nom de fichier à sqlite3_open
et assurez-vous que TEMP_STORE est correctement défini
(Tout le texte ci-dessus est extrait de ma propre réponse à une question distincte liée à sqlite )
J'ai testé quelques pragmas proposés dans les réponses ici:
synchronous = OFF
journal_mode = WAL
journal_mode = OFF
locking_mode = EXCLUSIVE
synchronous = OFF
+ locking_mode = EXCLUSIVE
+ journal_mode = OFF
Voici mes chiffres pour différents nombres d'insertions dans une transaction:
L'augmentation de la taille du lot peut vous donner une véritable amélioration des performances, tout en désactivant le journal, la synchronisation, l'acquisition d'un verrouillage exclusif donnera un gain insignifiant. Les points autour de ~ 110k montrent comment la charge d'arrière-plan aléatoire peut affecter les performances de votre base de données.
Il convient également de mentionner que journal_mode=WAL
est une bonne alternative aux valeurs par défaut. Il donne un certain gain, mais ne réduit pas la fiabilité.
J'ai trouvé que c'était un bon mélange pour une importation longue.
.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
source: http://erictheturtle.blogspot.be/2009/05/fastest-bulk-import-into-sqlite.html
quelques informations supplémentaires: http://blog.quibb.org/2010/08/fast-bulk-inserts-into-sqlite/
Si vous insérez juste une fois, j'ai peut-être un sale tour pour vous.
L'idée est simple, tout d'abord insérer dans une base de données mémoire, puis sauvegarder et enfin restaurer dans votre fichier de base de données d'origine.
J'ai écrit les étapes détaillées sur mon blog . :)