¿Es posible insertar varias filas a la vez en una base de datos SQLite?

551

En MySQL puede insertar varias filas de esta manera:

INSERT INTO 'tablename' ('column1', 'column2') VALUES
    ('data1', 'data2'),
    ('data1', 'data2'),
    ('data1', 'data2'),
    ('data1', 'data2');

Sin embargo, recibo un error cuando intento hacer algo como esto. ¿Es posible insertar varias filas a la vez en una base de datos SQLite? ¿Cuál es la sintaxis para hacer eso?

Andrés
fuente
26
No es un engaño, ya que esta pregunta es sobre SQLite específicamente, a diferencia de SQL en general (aunque algunas de las respuestas a esa pregunta son útiles para esta).
Brian Campbell
55
En insertos a granel: stackoverflow.com/questions/1711631/…
tuinstoel
55
La pregunta es por qué deberías hacer eso. Como SQlite está en proceso en la misma máquina y puede insertar múltiples inserciones en una transacción, ¿no veo la necesidad?
andig
2
Sí, comience desde la versión 2012-03-20 (3.7.11), su sintaxis es compatible.
mjb

Respuestas:

607

actualizar

Como BrianCampbell señala aquí , SQLite 3.7.11 y superior ahora admite la sintaxis más simple de la publicación original . Sin embargo, el enfoque que se muestra sigue siendo apropiado si desea la máxima compatibilidad entre las bases de datos heredadas.

respuesta original

Si tuviera privilegios, me encontraría con la respuesta de river : puede insertar varias filas en SQLite, solo necesita una sintaxis diferente . Para dejarlo perfectamente claro, el ejemplo de MySQL de OP:

INSERT INTO 'tablename' ('column1', 'column2') VALUES
  ('data1', 'data2'),
  ('data1', 'data2'),
  ('data1', 'data2'),
  ('data1', 'data2');

Esto se puede refundir en SQLite como:

     INSERT INTO 'tablename'
          SELECT 'data1' AS 'column1', 'data2' AS 'column2'
UNION ALL SELECT 'data1', 'data2'
UNION ALL SELECT 'data1', 'data2'
UNION ALL SELECT 'data1', 'data2'

una nota sobre el rendimiento

Originalmente usé esta técnica para cargar eficientemente grandes conjuntos de datos de Ruby on Rails. Sin embargo , como señala Jaime Cook , no está claro si se trata de un individuo de envoltura más rápido INSERTsdentro de una sola transacción:

BEGIN TRANSACTION;
INSERT INTO 'tablename' table VALUES ('data1', 'data2');
INSERT INTO 'tablename' table VALUES ('data3', 'data4');
...
COMMIT;

Si su objetivo es la eficiencia, primero debe intentarlo.

una nota sobre UNION vs UNION ALL

Como comentaron varias personas, si usa UNION ALL(como se muestra arriba), se insertarán todas las filas, por lo que en este caso, obtendría cuatro filas de data1, data2. Si omite el ALL, se eliminarán las filas duplicadas (y la operación presumiblemente será un poco más lenta). Estamos utilizando UNION ALL ya que coincide más con la semántica de la publicación original.

Para concluir

PD: Por favor, haga +1 en la respuesta de river , ya que presentó la solución primero.

temerario
fuente
102
Como nota adicional, sqlite solo parece admitir hasta 500 de estas selecciones de unión por consulta, por lo que si está tratando de agregar más datos que los necesarios, deberá dividirlos en bloques de 500 elementos ( sqlite.org/limits.html )
Jamie Cook
3
De acuerdo: SQLite no es el cuello de botella, es la sobrecarga de las transacciones ORM individuales (en mi caso, Ruby On Rails). Entonces sigue siendo una gran victoria.
fearless_fool
3
¿Cómo se compara esta solución o la solución 3.7.11 con el uso de bloques de transacciones? ¿Es más rápido insert into t values (data),(data),(data)o begin transaction; insert into t values (data);insert into t values (data);insert into t values (data);Commit;?
Dan
55
Como otra nota: ¡ten cuidado! ¡esta sintaxis elimina filas duplicadas! use UNION ALLpara evitar eso (o para algunas ganancias menores de rendimiento).
chacham15
1
@Shadowfax para verificar la versión de SQLite, mire sqlite3.sqlite_version(debe ser 3.7.xo 3.8.x), no sqlite3.version(que es solo la versión del módulo de Python).
max
562

Sí, es posible, pero no con los valores de inserción separados por comas habituales.

Prueba esto...

insert into myTable (col1,col2) 
     select aValue as col1,anotherValue as col2 
     union select moreValue,evenMoreValue 
     union...

Sí, es un poco feo pero lo suficientemente fácil como para automatizar la generación de la declaración a partir de un conjunto de valores. Además, parece que solo necesita declarar los nombres de columna en la primera selección.

río
fuente
36
por favor UNION ALLno use UNION. Excepto si desea eliminar duplicados.
Benoit
44
Si desea que los ID se incrementen automáticamente, asígneles un valor NULL.
lenooh
241

Sí, a partir de SQLite 3.7.11 esto es compatible con SQLite. De la documentación de SQLite :

Sintaxis de la instrucción SQLite INSERT

(cuando esta respuesta se escribió originalmente, esto no fue compatible)

Para la compatibilidad con versiones anteriores de SQLite, puede utilizar el truco sugerido por Andy y fearless_fool usando UNION, pero para 3.7.11 y más tarde la sintaxis más simple descrito aquí debe ser preferido.

Brian Campbell
fuente
3
Diría que el diagrama permite varias filas, ya que hay un bucle cerrado con una coma fuera de los paréntesis después VALUES.
Johannes Gerer
@JohannesGerer Han actualizado esta imagen desde que la inserté. Puede ver el diagrama en el momento en que lo inserté en Internet Archive . De hecho, fue hace solo dos meses que agregaron soporte para varias filas en una inserción, y solo hace dos días que lanzaron una versión con ese cambio. Actualizaré mi respuesta en consecuencia.
Brian Campbell
1
@Brian, ¿podrías por favor citar el texto de la documentación de SQLite, que dice que esto ES posible? Volví a leer los documentos de inserción 3 veces y no encontré nada sobre insertar varias filas, pero solo esta imagen (y nada sobre comas VALUES (...), (...)) :(
Prizoff
1
@Prizoff Me vinculé al commit en el que se agregó este soporte , incluidos los casos de prueba. Puede ver en el diagrama (compare el enlace IA ) que hay un bucle alrededor de la expresión después VALUES, lo que indica que puede repetirse separada por comas. Y me vinculé a las notas de la versión para agregar la función, que dice "Mejorar la sintaxis INSERT para permitir que se inserten varias filas a través de la cláusula VALUES".
Brian Campbell
1
@Prizoff Le mencioné esto al mantenedor de SQLite, él ha comprometido una solución que está disponible en el borrador de la documentación . Supongo que estará en la documentación oficial a partir del próximo lanzamiento.
Brian Campbell
57

Escribí un código ruby ​​para generar una sola inserción de varias filas de 500 elementos a partir de una serie de instrucciones de inserción que fue considerablemente más rápido que ejecutar las inserciones individuales. Luego intenté simplemente envolver las múltiples inserciones en una sola transacción y descubrí que podía obtener el mismo tipo de velocidad con considerablemente menos código.

BEGIN TRANSACTION;
INSERT INTO table VALUES (1,1,1,1);
INSERT INTO table VALUES (2,2,2,2);
...
COMMIT;
Jamie Cook
fuente
1
pero esto no funcionó en el código, mientras que funciona directamente en el administrador de SQLite. En el código solo inserta la primera fila :(
Vaibhav Saran
44
Estoy usando este estilo de inserción en mi código y funciona perfectamente. Solo tiene que asegurarse de enviar TODOS los SQL a la vez. Este fue un gran aumento de velocidad para mí, pero tengo curiosidad por saber si la respuesta aceptada es más rápida o más lenta que esto.
Dan
Este enfoque se escala muy bien cuando se modifican varias tablas dentro de una sola transacción, lo que me sucede a menudo al cargar por lotes una base de datos.
Frank
Tenga en cuenta que este enfoque no funcionará si necesita utilizar enlaces . El motor SQLite3 asumirá que todos sus enlaces se aplicarán en la primera instrucción e ignorará las siguientes. Vea esta pregunta SO para una explicación más detallada.
Philippe Hebert
38

De acuerdo con esta página no es compatible:

  • 2007-12-03: INSERT de varias filas, también conocido como INSERT compuesto, no es compatible.
  INSERT INTO table (col1, col2) VALUES 
      ('row1col1', 'row1col2'), ('row2col1', 'row2col2'), ...

En realidad, de acuerdo con el estándar SQL92, una expresión VALUES debería poder sostenerse sobre sí misma. Por ejemplo, lo siguiente debería devolver una tabla de una columna con tres filas:VALUES 'john', 'mary', 'paul';

Desde la versión 3.7.11 SQLite hace el apoyo de varias filas a insertar . Richard Hipp comenta:

"El nuevo inserto de valores múltiples es simplemente una sugerencia sintáctica (sic) para el inserto compuesto. No hay una ventaja de rendimiento de una forma u otra".

typeseven
fuente
No hay una ventaja de rendimiento de una forma u otra. - ¿Podrías decirme dónde viste ese comentario? No pude encontrarlo en ningún lado.
Alix Axel
15

Comience desde la versión 2012-03-20 (3.7.11), sqlite admite la siguiente sintaxis INSERT:

INSERT INTO 'tablename' ('column1', 'column2') VALUES
  ('data1', 'data2'),
  ('data3', 'data4'),
  ('data5', 'data6'),
  ('data7', 'data8');

Lea la documentación: http://www.sqlite.org/lang_insert.html

PD: Por favor +1 a la respuesta / respuesta de Brian Campbell. ¡no es mio! Presentó la solución primero.

mjb
fuente
10

Como han dicho los otros carteles, SQLite no admite esta sintaxis. No sé si inserciones compuestas son parte del estándar SQL, pero en mi experiencia que están no implementado en muchos productos.

Además, debe tener en cuenta que el rendimiento de INSERT en SQLite mejora considerablemente si ajusta varios INSERT en una transacción explícita.

Larry Lustig
fuente
10

Sí, sql puede hacer esto, pero con una sintaxis diferente. La documentación de sqlite es bastante buena, por cierto. También le indicará que la única forma de insertar varias filas es usar una instrucción select como la fuente de los datos que se insertarán.

innaM
fuente
9

Sqlite3 no puede hacer eso directamente en SQL, excepto a través de SELECT, y aunque SELECT puede devolver una "fila" de expresiones, no conozco ninguna forma de hacer que devuelva una columna falsa.

Sin embargo, la CLI puede hacerlo:

.import FILE TABLE     Import data from FILE into TABLE
.separator STRING      Change separator used by output mode and .import

$ sqlite3 /tmp/test.db
SQLite version 3.5.9
Enter ".help" for instructions
sqlite> create table abc (a);
sqlite> .import /dev/tty abc
1
2
3
99
^D
sqlite> select * from abc;
1
2
3
99
sqlite> 

Si coloca un bucle alrededor de un INSERT, en lugar de usar el .importcomando CLI , asegúrese de seguir los consejos de las preguntas frecuentes de sqlite para la velocidad de INSERT:

Por defecto, cada instrucción INSERT es su propia transacción. Pero si rodea varias instrucciones INSERT con BEGIN ... COMMIT, todas las inserciones se agrupan en una sola transacción. El tiempo necesario para confirmar la transacción se amortiza en todas las declaraciones de inserción adjuntas y, por lo tanto, el tiempo por declaración de inserción se reduce considerablemente.

Otra opción es ejecutar PRAGMA síncrono = OFF. Este comando hará que SQLite no espere que los datos lleguen a la superficie del disco, lo que hará que las operaciones de escritura parezcan mucho más rápidas. Pero si pierde energía en medio de una transacción, su archivo de base de datos podría dañarse.

DigitalRoss
fuente
Si observa el código fuente del .importcomando SQLite , es solo un bucle, que lee una línea del archivo de entrada (o tty) y luego una instrucción INSERT para esa línea. Lamentablemente no mejora significativamente la eficiencia.
Bill Karwin
8

Alex tiene razón: la declaración "select ... union" perderá el orden, lo cual es muy importante para algunos usuarios. Incluso cuando inserta en un orden específico, sqlite cambia las cosas, así que prefiera usar transacciones si el orden de inserción es importante.

create table t_example (qid int not null, primary key (qid));
begin transaction;
insert into "t_example" (qid) values (8);
insert into "t_example" (qid) values (4);
insert into "t_example" (qid) values (9);
end transaction;    

select rowid,* from t_example;
1|8
2|4
3|9
AG1
fuente
8

fearless_fool tiene una excelente respuesta para versiones anteriores. Solo quería agregar que debe asegurarse de tener todas las columnas enumeradas. Entonces, si tiene 3 columnas, debe asegurarse de que select actúa en 3 columnas.

Ejemplo: tengo 3 columnas pero solo quiero insertar 2 columnas de datos. Supongamos que no me importa la primera columna porque es una identificación entera estándar. Podría hacer lo siguiente ...

INSERT INTO 'tablename'
      SELECT NULL AS 'column1', 'data1' AS 'column2', 'data2' AS 'column3'
UNION SELECT NULL, 'data3', 'data4'
UNION SELECT NULL, 'data5', 'data6'
UNION SELECT NULL, 'data7', 'data8'

Nota: Recuerde que la declaración "select ... union" perderá el orden. (De AG1)

LEÓN
fuente
7
INSERT INTO TABLE_NAME 
            (DATA1, 
             DATA2) 
VALUES      (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2); 
aasai arun
fuente
6

No puedes pero no creo que extrañes nada.

Debido a que llama a sqlite siempre en proceso, casi no importa en el rendimiento si ejecuta 1 instrucción de inserción o 100 instrucciones de inserción. Sin embargo, la confirmación lleva mucho tiempo, así que inserte esos 100 insertos dentro de una transacción.

Sqlite es mucho más rápido cuando usa consultas parametrizadas (se necesita mucho menos análisis), por lo que no concatenaría declaraciones grandes como esta:

insert into mytable (col1, col2)
select 'a','b'
union 
select 'c','d'
union ...

Deben analizarse una y otra vez porque cada declaración concatenada es diferente.

tuinstoel
fuente
6

en mysql lite no puede insertar múltiples valores, pero puede ahorrar tiempo abriendo la conexión solo una vez y luego haciendo todas las inserciones y luego cerrando la conexión. Ahorra mucho tiempo

g.revolución
fuente
5

El problema con el uso de la transacción es que bloquea la tabla también para leer. Entonces, si tiene muchos datos para insertar y necesita acceder a sus datos, por ejemplo, una vista previa más o menos, esta forma no funciona bien.

El problema con la otra solución es que pierde el orden de inserción

insert into mytable (col)
select 'c'
union 
select 'd'
union 
select 'a'
union 
select 'b';

En el sqlite los datos se almacenarán a, b, c, d ...

alex
fuente
5

A partir de la versión 3.7.11, SQLite admite la inserción de varias filas. Richard Hipp comenta:

Estoy usando 3.6.13

Yo mando así:

insert into xtable(f1,f2,f3) select v1 as f1, v2 as f2, v3 as f3 
union select nextV1+, nextV2+, nextV3+

Con 50 registros insertados a la vez, solo lleva un segundo o menos.

Es cierto que usar sqlite para insertar varias filas a la vez es muy posible. Por @Andy escribió.

gracias Andy +1

XenKid
fuente
4
INSERT INTO tabela(coluna1,coluna2) 
SELECT 'texto','outro'
UNION ALL 
SELECT 'mais texto','novo texto';
ademar111190
fuente
2

Tengo una consulta como la siguiente, pero con el controlador ODBC, SQLite tiene un error con "", dice. Ejecuto vbscript en HTA (aplicación HTML).

INSERT INTO evrak_ilac_iliskileri (evrak_id, ilac_id, baglayan_kullanici_id, tarih) VALUES (4150,762,1,datetime()),(4150,9770,1,datetime()),(4150,6609,1,datetime()),(4150,3628,1,datetime()),(4150,9422,1,datetime())
caglaror
fuente
2

En sqlite 3.7.2:

INSERT INTO table_name (column1, column2) 
                SELECT 'value1', 'value1' 
          UNION SELECT 'value2', 'value2' 
          UNION SELECT 'value3', 'value3' 

y así

ashakirov
fuente
2

Puedo hacer que la consulta sea dinámica. Esta es mi mesa:

CREATE TABLE "tblPlanner" ("probid" text,"userid" TEXT,"selectedtime" DATETIME,"plannerid" TEXT,"isLocal" BOOL,"applicationid" TEXT, "comment" TEXT, "subject" TEXT)

y obtengo todos los datos a través de un JSON, así que después de obtener todo dentro de un NSArrayseguí esto:

    NSMutableString *query = [[NSMutableString alloc]init];
    for (int i = 0; i < arr.count; i++)
    {
        NSString *sqlQuery = nil;
        sqlQuery = [NSString stringWithFormat:@" ('%@', '%@', '%@', '%@', '%@', '%@', '%@', '%@'),",
                    [[arr objectAtIndex:i] objectForKey:@"plannerid"],
                    [[arr objectAtIndex:i] objectForKey:@"probid"],
                    [[arr objectAtIndex:i] objectForKey:@"userid"],
                    [[arr objectAtIndex:i] objectForKey:@"selectedtime"],
                    [[arr objectAtIndex:i] objectForKey:@"isLocal"],
                    [[arr objectAtIndex:i] objectForKey:@"subject"],
                    [[arr objectAtIndex:i] objectForKey:@"comment"],
                    [[NSUserDefaults standardUserDefaults] objectForKey:@"applicationid"]
                    ];
        [query appendString:sqlQuery];
    }
    // REMOVING LAST COMMA NOW
    [query deleteCharactersInRange:NSMakeRange([query length]-1, 1)];

    query = [NSString stringWithFormat:@"insert into tblPlanner (plannerid, probid, userid, selectedtime, isLocal, applicationid, subject, comment) values%@",query];

Y finalmente la consulta de salida es esta:

insert into tblPlanner (plannerid, probid, userid, selectedtime, isLocal, applicationid, subject, comment) values 
<append 1>
('pl1176428260', '', 'US32552', '2013-06-08 12:00:44 +0000', '0', 'subj', 'Hiss', 'ap19788'),
<append 2>
('pl2050411638', '', 'US32552', '2013-05-20 10:45:55 +0000', '0', 'TERI', 'Yahoooooooooo', 'ap19788'), 
<append 3>
('pl1828600651', '', 'US32552', '2013-05-21 11:33:33 +0000', '0', 'test', 'Yest', 'ap19788'),
<append 4>
('pl549085534', '', 'US32552', '2013-05-19 11:45:04 +0000', '0', 'subj', 'Comment', 'ap19788'), 
<append 5>
('pl665538927', '', 'US32552', '2013-05-29 11:45:41 +0000', '0', 'subj', '1234567890', 'ap19788'), 
<append 6>
('pl1969438050', '', 'US32552', '2013-06-01 12:00:18 +0000', '0', 'subj', 'Cmt', 'ap19788'),
<append 7>
('pl672204050', '', 'US55240280', '2013-05-23 12:15:58 +0000', '0', 'aassdd', 'Cmt', 'ap19788'), 
<append 8>
('pl1019026150', '', 'US32552', '2013-06-08 12:15:54 +0000', '0', 'exists', 'Cmt', 'ap19788'), 
<append 9>
('pl790670523', '', 'US55240280', '2013-05-26 12:30:21 +0000', '0', 'qwerty', 'Cmt', 'ap19788')

que también funciona bien a través del código y puedo guardar todo en SQLite con éxito.

Antes de esto, hice las UNIONconsultas dinámicas, pero eso comenzó a dar algunos errores de sintaxis. De todos modos, esto está funcionando bien para mí.

Vaibhav Saran
fuente
2

Me sorprende que nadie haya mencionado declaraciones preparadas . A menos que esté utilizando SQL por sí solo y no dentro de ningún otro lenguaje, entonces pensaría que las declaraciones preparadas envueltas en una transacción serían la forma más eficiente de insertar múltiples filas.


fuente
1
Las declaraciones preparadas siempre son una buena idea, pero no están relacionadas en absoluto con la pregunta que hace el OP. Él pregunta cuál es la sintaxis básica para insertar múltiples datos en una declaración.
Lakey
-1

Si está utilizando bash shell, puede usar esto:

time bash -c $'
FILE=/dev/shm/test.db
sqlite3 $FILE "create table if not exists tab(id int);"
sqlite3 $FILE "insert into tab values (1),(2)"
for i in 1 2 3 4; do sqlite3 $FILE "INSERT INTO tab (id) select (a.id+b.id+c.id)*abs(random()%1e7) from tab a, tab b, tab c limit 5e5"; done; 
sqlite3 $FILE "select count(*) from tab;"'

O si está en sqlite CLI, entonces necesita hacer esto:

create table if not exists tab(id int);"
insert into tab values (1),(2);
INSERT INTO tab (id) select (a.id+b.id+c.id)*abs(random()%1e7) from tab a, tab b, tab c limit 5e5;
INSERT INTO tab (id) select (a.id+b.id+c.id)*abs(random()%1e7) from tab a, tab b, tab c limit 5e5;
INSERT INTO tab (id) select (a.id+b.id+c.id)*abs(random()%1e7) from tab a, tab b, tab c limit 5e5;
INSERT INTO tab (id) select (a.id+b.id+c.id)*abs(random()%1e7) from tab a, tab b, tab c limit 5e5;
select count(*) from tab;

¿Como funciona? Utiliza esa tabla if tab:

id int
------
1
2

luego select a.id, b.id from tab a, tab bregresa

a.id int | b.id int
------------------
    1    | 1
    2    | 1
    1    | 2
    2    | 2

y así. Después de la primera ejecución, insertamos 2 filas, luego 2 ^ 3 = 8. (tres porque tenemos tab a, tab b, tab c)

Después de la segunda ejecución, insertamos (2+8)^3=1000filas adicionales

Después de esto, insertamos las max(1000^3, 5e5)=500000filas, etc.

Este es el método más rápido conocido para mí de poblar bases de datos SQLite.

test30
fuente
2
Esto no funciona si desea insertar datos útiles .
CL.
@CL. no es verdad. puedes mezclarlo con fechas aleatorias e identificadores como quieras.
test30