web-dev-qa-db-fra.com

Inserts en vrac plus rapides dans sqlite3?

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?

42
scubabbl

Vous pouvez également essayer peaufiner quelques paramètres pour obtenir une vitesse supplémentaire. Plus précisément, vous voulez probablement PRAGMA synchronous = OFF;.

21
user42092
  • encapsuler tous les INSERT dans une transaction, même s'il n'y a qu'un seul utilisateur, c'est beaucoup plus rapide.
  • utiliser des déclarations préparées.
57
Javier

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.

35
ramanujan
  • 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.

  • Utilisez des instructions SQL compilées pour effectuer les insertions.
  • Enfin, comme déjà mentionné, si vous êtes prêt à renoncer à la pleine conformité ACID, définissez PRAGMA synchronous = OFF;.
18
paxos1977

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

10
Hannes de Jager

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.

5
scott

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;
3
Flavien Volken

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 )

3
pestophagous

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

Code C #.

3
astef

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/

1
maazza

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 . :)

0
circle