2008-12-12 22 views
38

Tengo un archivo de aproximadamente 30000 líneas de datos que quiero cargar en una base de datos sqlite3. ¿Hay una forma más rápida que generar instrucciones de inserción para cada línea de datos?Inserciones a granel más rápidas en sqlite3?

Los datos están delimitados por espacios y se asignan directamente a una tabla sqlite3. ¿Hay algún tipo de método de inserción masiva para agregar datos de volumen a una base de datos?

¿Alguien ha ideado alguna forma misteriosamente maravillosa de hacer esto si no está incorporada?

Debo preceder esto preguntando, ¿hay alguna forma C++ de hacerlo desde la API?

Respuesta

19

También puedes probar tweaking a few parameters para obtener mayor velocidad. Específicamente, probablemente desee PRAGMA synchronous = OFF;.

+20

pragma synchronous = OFF es una mala idea: apenas afectará el rendimiento de las inserciones en bloque, y su base de datos se dañará en caso de un corte de energía. Una idea mucho mejor es envolver sus insertos en una transacción. –

+10

Envolviendo INSERTOS en una TRANSACCIÓN y usando PRAGMA journal_mode = MEMORY; Evitará que los INSERT peguen en el disco hasta el final de la transacción. – Ted

+3

Tenga en cuenta que MEMORY dañará db en un corte de energía –

54
  • envuelven todos los INSERT en una transacción, incluso si hay un solo usuario, es mucho más rápido.
  • usa declaraciones preparadas.
+0

cierto para la mayoría (todos?) Bases de datos SQL. – stesch

+1

PRAGMA journal_mode = MEMORIA; Puede ser útil para algunas personas – Ted

17
  • Aumentar PRAGMA default_cache_size a un número mucho mayor. Esto hará que aumente el número de páginas almacenadas en la memoria caché .

  • Encapsule todas las inserciones en una sola transacción en lugar de una transacción por fila.

  • Usa instrucciones SQL compiladas para hacer las inserciones.
  • Finalmente, como ya se mencionó, si está dispuesto a renunciar a la conformidad completa de ACID, configure PRAGMA synchronous = OFF;.
2

Dependiendo del tamaño de los datos y la cantidad de RAM disponible, una de las mejores ganancias de rendimiento ocurrirá al establecer sqlite para usar una base de datos todo en memoria en lugar de escribir en el disco.

Para bases de datos en memoria, pase NULL como el argumento de nombre de archivo para sqlite3_open y make sure that TEMP_STORE is defined appropriately

(Todo el texto anterior es un extracto de mi propia respuesta a una separate sqlite-related question)

+1

El enlace apunta a un documento incompleto. Hay menos información de la que uno esperaría, – Richard

33

Usted desea utilizar el .import mando. Por ejemplo:

$ 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 

Tenga en cuenta que este comando de carga masiva no es SQL sino una característica personalizada de SQLite. Como tal, tiene una sintaxis extraña porque la estamos pasando por echo al intérprete de línea de comando interactivo, sqlite3.

En PostgreSQL es el equivalente COPY FROM: http://www.postgresql.org/docs/8.1/static/sql-copy.html

En MySQL es LOAD DATA LOCAL INFILE: http://dev.mysql.com/doc/refman/5.1/en/load-data.html

Una última cosa: recordar que tener cuidado con el valor de .separator. Ese es un problema muy común al hacer inserciones a granel.

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

Es necesario configurar explícitamente el separador de ser un espacio, ficha, o una coma antes de hacer .import.

+0

+1, gran respuesta – torial

+1

Esto es genial, y muy rápido. 20 minutos reducidos a 3 segundos. – Gazzer

+3

'.show' en lugar de' .show .separator' trabajado para mí – Gazzer

0

Si solo está insertando una vez, es posible que tenga un truco sucio para usted.

La idea es simple, primero insertando en una base de datos de memoria, luego haga una copia de seguridad y finalmente restaure su archivo de base de datos original.

Escribí los pasos detallados en my blog. :)

5

No hay manera de inserción masiva, pero hay una manera de escribir grandes trozos a la memoria, a continuación, confirmar que la base de datos . Para la API de C/C++, solo haga lo siguiente:

sqlite3_exec (db, "BEGIN TRANSACTION", NULL, NULL, NULL);

... (instrucciones INSERT)

sqlite3_exec (db, "COMMIT TRANSACTION", null, null, null);

Asumiendo db es su puntero a la base de datos.

9

RE: "¿Hay una forma más rápida de generar instrucciones de inserción para cada línea de datos?"

Primero: Reduzca a 2 declaraciones SQL haciendo uso de Virtual table API de Sqlite3, por ejemplo.

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

La idea aquí es que implemente una interfaz C que lee establecen los datos de origen y presentarlo a SQLite como una tabla virtual y luego hacer una copia de SQL desde la fuente a la tabla de destino de una sola vez. Suena más difícil de lo que realmente es y he medido las mejoras de gran velocidad de esta manera.

Segundo: Utilice los otros consejos proporcionados aquí, es decir, la configuración de pragma y el uso de una transacción.

Tercero: Tal vez vea si puede eliminar algunos de los índices en la tabla de destino. De esa manera, sqlite tendrá menos índices para actualizar para cada fila insertada

+0

+1 esta es en realidad una forma "c" de hacerlo desde la API (como se solicita), buena – AlexD

3

Un buen compromiso es envolver sus INSERTS entre BEGIN; y punto; palabra clave es decir:

BEGIN; 
INSERT INTO table VALUES(); 
INSERT INTO table VALUES(); 
... 
END; 
+0

También hay 'INSERT INTO table VALUES(),(),();' – ZN13

0

He encontrado que esta es una buena combinación para una importación larga.

.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 

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

algo de información adicional: http://blog.quibb.org/2010/08/fast-bulk-inserts-into-sqlite/

Cuestiones relacionadas