É possível inserir várias linhas ao mesmo tempo em um banco de dados SQLite?

551

No MySQL, você pode inserir várias linhas como esta:

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

No entanto, estou recebendo um erro ao tentar fazer algo assim. É possível inserir várias linhas ao mesmo tempo em um banco de dados SQLite? Qual é a sintaxe para fazer isso?

Andrew
fonte
26
Não é um tolo, pois esta pergunta está perguntando sobre o SQLite especificamente, em oposição ao SQL em geral (embora algumas das respostas para essa pergunta sejam úteis para essa).
22909 Brian Campbell
5
Em inserções em massa: stackoverflow.com/questions/1711631/…
tuinstoel 22/11/2009
5
A questão é por que você deve fazer isso. Como o SQlite está em processo na mesma máquina e você pode agrupar várias inserções em uma transação, não vejo a necessidade?
22712 andig
2
Sim, a partir da versão 2012-03-20 (3.7.11), sua sintaxe é suportada.
Mjb

Respostas:

607

atualizar

Como BrianCampbell aponta aqui , o SQLite 3.7.11 e superior agora suporta a sintaxe mais simples da postagem original . No entanto, a abordagem mostrada ainda é apropriada se você deseja compatibilidade máxima entre os bancos de dados herdados.

resposta original

Se eu tivesse privilégios, responderia à resposta do river : Você pode inserir várias linhas no SQLite, mas precisa de sintaxe diferente . Para deixar perfeitamente claro, o exemplo do OPs MySQL:

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

Isso pode ser reformulado no 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'

uma nota sobre desempenho

Eu originalmente usei essa técnica para carregar com eficiência grandes conjuntos de dados do Ruby on Rails. No entanto , como Jaime Cook ressalta , não está claro que seja um indivíduo de quebra automática mais rápido INSERTsem uma única transação:

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

Se a eficiência é seu objetivo, tente primeiro.

uma nota sobre UNION vs UNION ALL

Como várias pessoas comentaram, se você usar UNION ALL(como mostrado acima), todas as linhas serão inseridas; nesse caso, você obterá quatro linhas de data1, data2. Se você omitir o ALL, as linhas duplicadas serão eliminadas (e a operação presumivelmente será um pouco mais lenta). Estamos usando o UNION ALL, pois ele se aproxima mais da semântica da postagem original.

no fechamento

PS: Favor marcar com +1 a resposta do river , pois ela apresentou a solução primeiro.

fearless_fool
fonte
102
Como uma observação adicional, o sqlite parece suportar até 500 seleções de união por consulta; portanto, se você estiver tentando inserir mais dados do que os necessários, é necessário dividi-los em 500 blocos de elementos ( sqlite.org/limits.html )
Jamie Cook
3
Concordo: SQLite não é o gargalo, é a sobrecarga de transações ORM individuais (no meu caso, Ruby On Rails). Portanto, ainda é uma grande vitória.
fearless_fool
3
Como essa solução ou a solução 3.7.11 se compara ao uso de blocos de transações? é mais rápido insert into t values (data),(data),(data)ou begin transaction; insert into t values (data);insert into t values (data);insert into t values (data);Commit;?
1077 Dan
5
Como nota adicional: tenha cuidado! essa sintaxe remove linhas duplicadas! use UNION ALLpara evitar isso (ou para alguns pequenos ganhos de desempenho).
chacham15
1
@ Shadowfax para verificar a versão do SQLite, veja sqlite3.sqlite_version(deve ser 3.7.x ou 3.8.x), não sqlite3.version(que é apenas a versão do módulo python).
Max
562

Sim, é possível, mas não com os valores usuais de inserção separados por vírgula.

Tente isso ...

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

Sim, é um pouco feio, mas fácil o suficiente para automatizar a geração da declaração a partir de um conjunto de valores. Além disso, parece que você só precisa declarar os nomes das colunas na primeira seleção.

rio
fonte
36
por favor UNION ALLnão use UNION. Exceto se você deseja remover duplicatas.
Benoit
4
Se você deseja que os IDs aumentem automaticamente, atribua a eles um valor NULL.
lenooh 8/09/15
241

Sim, a partir do SQLite 3.7.11, isso é suportado no SQLite. Na documentação do SQLite :

Sintaxe da instrução SQLite INSERT

(quando esta resposta foi originalmente escrita, isso não era suportado)

Para compatibilidade com versões mais antigas do SQLite, você pode usar o truque sugerido por andy e fearless_fool usando UNION, mas para a versão 3.7.11 e posterior, a sintaxe mais simples descrita aqui deve ser preferida.

Brian Campbell
fonte
3
Eu diria que o diagrama permite várias linhas, pois há um loop fechado com uma vírgula fora dos parênteses depois VALUES.
Johannes Gerer 22/03/12
@JohannesGerer Eles atualizam esta imagem desde que a incorporei. Você pode ver o diagrama no momento em que o integrei no Internet Archive . De fato, foi há apenas dois meses que eles adicionaram suporte para várias linhas em uma inserção e apenas dois dias atrás eles lançaram uma versão com essa alteração. Vou atualizar minha resposta de acordo.
Brian Campbell
1
@ Brian, você poderia citar o texto da documentação do SQLite, que afirma que isso é possível? Reli inserir docs 3 vezes e não encontrou nada sobre a inserção de várias linhas, mas apenas esta imagem (e nada sobre vírgula VALUES (...), (...)) :(
Prizoff
1
@Prizoff Vinculei ao commit no qual esse suporte foi adicionado , incluindo casos de teste. Você pode ver no diagrama (compare o link IA ), que existe um loop em torno da expressão depois VALUES, indicando que ela pode ser repetida separada por vírgulas. E vinculei às notas de lançamento da versão que adicionam o recurso, que afirma "Aprimore a sintaxe INSERT para permitir a inserção de várias linhas através da cláusula VALUES".
Brian Campbell
1
@Prizoff Mencionei isso para o mantenedor do SQLite, ele cometeu uma correção que está disponível no rascunho da documentação . Suponho que estará na documentação oficial a partir do próximo lançamento.
Brian Campbell
57

Escrevi um código ruby ​​para gerar uma inserção de várias linhas de 500 elementos a partir de uma série de instruções de inserção que era consideravelmente mais rápida do que executar as inserções individuais. Depois, tentei simplesmente agrupar as várias inserções em uma única transação e descobri que podia obter o mesmo tipo de velocidade com consideravelmente menos código.

BEGIN TRANSACTION;
INSERT INTO table VALUES (1,1,1,1);
INSERT INTO table VALUES (2,2,2,2);
...
COMMIT;
Jamie Cook
fonte
1
mas isso não funcionou no código, enquanto trabalha diretamente no gerenciador SQLite. No código, ele apenas insere a 1ª linha :(
Vaibhav Saran
4
Estou usando esse estilo de inserção no meu código e funciona perfeitamente. Você só precisa se certificar de enviar TODO o SQL de uma só vez. Este foi um enorme aumento de velocidade para mim, mas estou curioso para saber se a resposta aceita é mais rápida ou mais lenta, então isso?
1010 Dan
Essa abordagem é muito bem dimensionada ao modificar várias tabelas em uma única transação, o que eu me vejo fazendo frequentemente ao carregar um banco de dados em lote.
19415 Frank
Observe que essa abordagem não funcionará se você precisar usar ligações . O mecanismo SQLite3 assumirá que todas as suas ligações devem ser aplicadas na primeira instrução e ignorará as seguintes instruções. Veja esta pergunta SO para uma explicação mais detalhada.
Philippe Hebert
38

De acordo com esta página , não é suportado:

  • 03-12-2007: INSERT de várias linhas, também conhecido como INSERT composto, não é suportado.
  INSERT INTO table (col1, col2) VALUES 
      ('row1col1', 'row1col2'), ('row2col1', 'row2col2'), ...

Na verdade, de acordo com o padrão SQL92, uma expressão VALUES deve ser capaz de se sustentar. Por exemplo, o seguinte deve retornar uma tabela de uma coluna com três linhas:VALUES 'john', 'mary', 'paul';

A partir da versão 3.7.11 SQLite faz suporte multi-row-insert . Richard Hipp comenta:

"A nova pastilha com múltiplos valores é apenas uma sugestão sintática (sic) para a pastilha composta. Não há vantagem de desempenho de uma maneira ou de outra."

tipos
fonte
Não há vantagem de desempenho de uma maneira ou de outra. - Você poderia me dizer onde viu esse comentário? Não encontrei em lugar algum.
Alix Axel
15

A partir da versão 2012-03-20 (3.7.11), o sqlite suporta a seguinte sintaxe INSERT:

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

Leia a documentação: http://www.sqlite.org/lang_insert.html

PS: Favor marcar com +1 a resposta / resposta de Brian Campbell. não é meu! Ele apresentou a solução primeiro.

mjb
fonte
10

Como os outros pôsteres disseram, o SQLite não suporta essa sintaxe. Não sei se os INSERTs compostos fazem parte do padrão SQL, mas, na minha experiência, eles não são implementados em muitos produtos.

Como um aparte, você deve estar ciente de que o desempenho do INSERT no SQLite será consideravelmente melhorado se você agrupar vários INSERTs em uma transação explícita.

Larry Lustig
fonte
10

Sim, o sql pode fazer isso, mas com uma sintaxe diferente. A documentação do sqlite é bastante boa, a propósito. Ele também irá dizer-lhe que a única maneira de inserir vários linha é usar uma instrução SELECT como a fonte dos dados a serem inseridos.

innaM
fonte
9

O Sqlite3 não pode fazer isso diretamente no SQL, exceto por meio de um SELECT, e embora o SELECT possa retornar uma "linha" de expressões, não conheço nenhuma maneira de fazê-lo retornar uma coluna falsa.

No entanto, a CLI pode fazer isso:

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

Se você colocar um loop em torno de um INSERT, em vez de usar o .importcomando CLI , siga as orientações das perguntas frequentes do sqlite para a velocidade do INSERT:

Por padrão, cada instrução INSERT é sua própria transação. Mas se você envolver várias instruções INSERT com BEGIN ... COMMIT, todas as inserções serão agrupadas em uma única transação. O tempo necessário para confirmar a transação é amortizado em todas as instruções de inserção incluídas e, portanto, o tempo por instrução de inserção é bastante reduzido.

Outra opção é executar o PRAGMA síncrono = OFF. Este comando fará com que o SQLite não espere que os dados cheguem à superfície do disco, o que fará com que as operações de gravação pareçam muito mais rápidas. Mas se você perder energia no meio de uma transação, o arquivo do banco de dados poderá ficar corrompido.

DigitalRoss
fonte
Se você olhar o código-fonte para o .importcomando do SQLite , é apenas um loop, lendo uma linha do arquivo de entrada (ou tty) e, em seguida, uma instrução INSERT para essa linha. Infelizmente, a eficiência não melhorou significativamente.
Bill Karwin
8

Alex está correto: a instrução "select ... union" perderá a ordem, o que é muito importante para alguns usuários. Mesmo quando você insere em uma ordem específica, o sqlite altera as coisas, portanto, prefira usar transações se a ordem de inserção for 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
fonte
8

fearless_fool tem uma ótima resposta para versões mais antigas. Eu só queria acrescentar que você precisa ter todas as colunas listadas. Portanto, se você tiver 3 colunas, verifique se a seleção atua em 3 colunas.

Exemplo: tenho 3 colunas, mas quero inserir apenas 2 colunas no valor de dados. Suponha que eu não me importo com a primeira coluna porque é um ID inteiro padrão. Eu poderia fazer o seguinte ...

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: Lembre-se de que a instrução "select ... union" perderá a ordem. (A partir de AG1)

LEO
fonte
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
fonte
6

Você não pode, mas acho que não sente nada.

Como você chama o sqlite sempre em processo, quase não importa no desempenho se você executa 1 instrução de inserção ou 100 instruções de inserção. A consolidação, no entanto, leva muito tempo, portanto, coloque essas 100 inserções em uma transação.

O sqlite é muito mais rápido quando você usa consultas parametrizadas (é necessário muito menos análise), então eu não concatenaria grandes instruções como esta:

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

Eles precisam ser analisados ​​repetidamente porque cada declaração concatenada é diferente.

tuinstoel
fonte
6

no mysql lite você não pode inserir múltiplos valores, mas pode economizar tempo abrindo a conexão apenas uma vez e, em seguida, fazendo todas as inserções e fechando a conexão. Isto economiza muito tempo

g.revolution
fonte
5

O problema com o uso da transação é que você bloqueia a tabela também para leitura. Portanto, se você tem realmente muitos dados para inserir e precisa acessá-los, por exemplo, uma pré-visualização ou mais, dessa maneira não funciona bem.

O problema com a outra solução é que você perde a ordem de inserção

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

No sqlite, os dados serão armazenados a, b, c, d ...

alex
fonte
5

A partir da versão 3.7.11, o SQLite suporta inserção de várias linhas. Richard Hipp comenta:

Estou usando 3.6.13

Eu comando assim:

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

Com 50 registros inseridos por vez, leva apenas um segundo ou menos.

É verdade que usar o sqlite para inserir várias linhas ao mesmo tempo é muito possível. Por @Andy escreveu.

obrigado Andy +1

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

Se você usar o plug-in do firefox do Sqlite manager , ele suporta inserções em massa de INSERTinstruções SQL.

De fato, ele não suporta isso, mas o Sqlite Browser suporta (funciona no Windows, OS X, Linux)

Chris S
fonte
2

Eu tenho uma consulta como abaixo, mas com o driver ODBC, SQLite tem um erro com "," diz. Eu executo vbscript no HTA (aplicativo 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
fonte
2

No sqlite 3.7.2:

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

e assim por diante

ashakirov
fonte
2

Eu sou capaz de tornar a consulta dinâmica. Esta é a minha mesa:

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

e estou obtendo todos os dados através de um JSON, então, depois de colocar tudo dentro de um NSArray, segui o seguinte:

    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];

E, finalmente, a consulta de saída é 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 também está funcionando bem através do código e sou capaz de salvar tudo no SQLite com êxito.

Antes disso, tornava o UNIONmaterial de consulta dinâmico, mas isso começou a dar algum erro de sintaxe. De qualquer forma, isso está funcionando bem para mim.

Vaibhav Saran
fonte
2

Estou surpreso que ninguém tenha mencionado declarações preparadas . A menos que você esteja usando o SQL sozinho e não em nenhuma outra linguagem, eu pensaria que instruções preparadas agrupadas em uma transação seriam a maneira mais eficiente de inserir várias linhas.


fonte
1
Declarações preparadas são sempre uma boa idéia, mas não estão relacionadas à pergunta que o OP está fazendo. Ele está perguntando qual é a sintaxe básica para inserir vários dados em uma instrução.
Lakey
-1

Se você estiver usando o bash shell, poderá usar o seguinte:

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;"'

Ou se você estiver na CLI do sqlite, precisará fazer o seguinte:

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? Faz uso dessa tabela if tab:

id int
------
1
2

então select a.id, b.id from tab a, tab bretorna

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

e assim por diante. Após a primeira execução, inserimos 2 linhas, depois 2 ^ 3 = 8. (três porque temos tab a, tab b, tab c)

Após a segunda execução, inserimos (2+8)^3=1000linhas adicionais

Tarde thrid inserimos sobre max(1000^3, 5e5)=500000linhas e assim por diante ...

Esse é o método mais rápido conhecido para preencher o banco de dados SQLite.

test30
fonte
2
Isso não funciona se você deseja inserir dados úteis .
CL.
@CL. não é verdade. você pode misturá-lo com datas e IDs aleatórios da maneira que desejar.
test30