È possibile inserire più righe alla volta in un database SQLite?


551

In MySQL puoi inserire più righe in questo modo:

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

Tuttavia, ricevo un errore quando provo a fare qualcosa del genere. È possibile inserire più righe alla volta in un database SQLite? Qual è la sintassi per farlo?


26
Non un duplicato, in quanto questa domanda si pone in particolare su SQLite, al contrario di SQL in generale (anche se alcune delle risposte a quella domanda sono utili per questo).
Brian Campbell,


5
La domanda è: perché dovresti farlo. Dato che SQlite è in elaborazione sullo stesso computer e puoi inserire più inserti in una transazione, non ne vedo la necessità?
Andig

2
Sì, a partire dalla versione 2012-03-20 (3.7.11), la sintassi è supportata.
mjb,

Risposte:


607

aggiornare

Come BrianCampbell sottolinea qui , SQLite 3.7.11 e versioni successive ora supportano la sintassi più semplice del post originale . Tuttavia, l'approccio mostrato è ancora appropriato se si desidera la massima compatibilità tra database legacy.

risposta originale

Se avessi i privilegi, risponderei alla risposta di River : puoi inserire più righe in SQLite, hai solo bisogno di una sintassi diversa . Per chiarire perfettamente, gli esempi di MySQL degli OP:

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

Questo può essere rifuso in SQLite come:

     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 sulle prestazioni

Inizialmente ho usato questa tecnica per caricare in modo efficiente set di dati di grandi dimensioni da Ruby on Rails. Tuttavia , come sottolinea Jaime Cook , non è chiaro che si tratti di un individuo più veloce INSERTsall'interno di una singola transazione:

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

Se l'efficienza è il tuo obiettivo, dovresti provare prima questo.

una nota su UNION vs UNION ALL

Come molte persone hanno commentato, se usi UNION ALL(come mostrato sopra), tutte le righe verranno inserite, quindi in questo caso otterrai quattro righe di data1, data2. Se si omette il ALL, le righe duplicate verranno eliminate (e l'operazione sarà presumibilmente un po 'più lenta). Stiamo utilizzando UNION ALL in quanto corrisponde maggiormente alla semantica del post originale.

in chiusura

PS: per favore fai +1 sulla risposta di river , poiché ha presentato prima la soluzione.


102
Come ulteriore nota, sqlite sembra supportare solo fino a 500 selezioni di unione di questo tipo per query, quindi se si sta tentando di inserire più dati di quelli, sarà necessario suddividerlo in 500 blocchi di elementi ( sqlite.org/limits.html )
Jamie Cook

3
D'accordo: SQLite non è il collo di bottiglia, è il sovraccarico delle singole transazioni ORM (nel mio caso, Ruby On Rails). Quindi è ancora una grande vittoria.
fearless_fool

3
Come si confronta questa soluzione o la soluzione 3.7.11 con l'utilizzo dei blocchi di transazione? è più veloce 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,

5
Come ulteriore nota: attenzione! questa sintassi rimuove le righe duplicate! usare UNION ALLper evitarlo (o per alcuni piccoli miglioramenti delle prestazioni).
chacham15,

1
@Shadowfax per verificare la versione di SQLite, guarda sqlite3.sqlite_version(dovrebbe essere 3.7.xo 3.8.x), non sqlite3.version(che è solo la versione del modulo python).
max

562

Sì, è possibile, ma non con i soliti valori di inserimento separati da virgola.

Prova questo...

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

Sì, è un po 'brutto ma abbastanza facile da automatizzare la generazione dell'istruzione da un insieme di valori. Inoltre, sembra che devi solo dichiarare i nomi delle colonne nella prima selezione.


36
per favore usa UNION ALLno UNION. Tranne se si desidera rimuovere i duplicati.
Benoit,

4
Se si desidera che gli ID si incrementino automaticamente, assegnare loro un valore NULL.
lenooh,

241

Sì, a partire da SQLite 3.7.11 questo è supportato in SQLite. Dalla documentazione di SQLite :

Sintassi dell'istruzione INSERT SQLite

(quando questa risposta era stata originariamente scritta, non era supportata)

Per compatibilità con le versioni precedenti di SQLite, è possibile utilizzare il trucco suggerito da andy e fearless_fool utilizzando UNION, ma per 3.7.11 e successive dovrebbe essere preferita la sintassi più semplice qui descritta.


3
Direi che il diagramma consente più righe, in quanto esiste un ciclo chiuso con una virgola fuori dalle parentesi dopo VALUES.
Johannes Gerer,

@JohannesGerer Hanno aggiornato questa immagine da quando l'ho incorporata. Puoi vedere il diagramma al momento in cui l'ho incorporato in Internet Archive . In effetti, solo due mesi fa hanno aggiunto il supporto per più righe in un inserto e solo due giorni fa hanno rilasciato una versione con quella modifica. Aggiornerò la mia risposta di conseguenza.
Brian Campbell,

1
@Brian, potresti per favore citare il testo della documentazione di SQLite, che afferma che questo è possibile? Ho riletto a inserire documenti 3 volte e non ho trovato nulla sull'inserimento di più righe, ma solo questa immagine (e niente sulla virgola in VALUES (...), (...)) :(
Prizoff

1
@Prizoff Ho collegato al commit in cui è stato aggiunto questo supporto , inclusi i casi di test. Si può vedere nel diagramma (confrontare il collegamento IA ), che dopo c'è un ciclo attorno all'espressione VALUES, che indica che può essere ripetuto separato da virgole. E ho collegato alle note di rilascio per la versione aggiungendo la funzione, che indicava "Migliora la sintassi INSERT per consentire l'inserimento di più righe tramite la clausola VALUES".
Brian Campbell,

1
@Prizoff Ne ho parlato con il manutentore di SQLite, ha commesso una correzione che è disponibile nella bozza di documentazione . Immagino che sarà nella documentazione ufficiale a partire dalla prossima versione.
Brian Campbell,

57

Ho scritto un po 'di codice rubino per generare un singolo inserto multi-riga di 500 elementi da una serie di istruzioni insert che era considerevolmente più veloce dell'esecuzione dei singoli inserti. Quindi ho provato semplicemente a racchiudere gli inserti multipli in una singola transazione e ho scoperto che avrei potuto ottenere lo stesso tipo di velocità con un considerevolmente meno codice.

BEGIN TRANSACTION;
INSERT INTO table VALUES (1,1,1,1);
INSERT INTO table VALUES (2,2,2,2);
...
COMMIT;

1
ma questo non ha funzionato nel codice, mentre funziona direttamente nel gestore SQLite. Nel codice inserisce solo la 1a riga :(
Vaibhav Saran,

4
Sto usando questo stile di inserimento nel mio codice e funziona perfettamente. Devi solo assicurarti di inviare TUTTI gli SQL contemporaneamente. Questo è stato un enorme aumento di velocità per me, ma sono curioso di sapere se la risposta accettata è più veloce o più lenta di questo?
Dan,

Questo approccio si adatta molto bene quando si modificano più tabelle all'interno di una singola transazione, cosa che mi ritrovo spesso a fare quando si carica in batch un database.
Frank,

Si noti che questo approccio non funzionerà se è necessario utilizzare i binding . Il motore SQLite3 supporrà che tutti i bind vengano applicati alla prima istruzione e ignorerà le seguenti dichiarazioni. Vedi questa domanda SO per una spiegazione più dettagliata.
Philippe Hebert,

38

Secondo questa pagina non è supportato:

  • 03-12-2007: INSERT multi-riga, ovvero INSERT composto, non supportato.
  INSERT INTO table (col1, col2) VALUES 
      ('row1col1', 'row1col2'), ('row2col1', 'row2col2'), ...

In realtà, secondo lo standard SQL92, un'espressione VALUES dovrebbe essere in grado di stare su se stessa. Ad esempio, quanto segue dovrebbe restituire una tabella a una colonna con tre righe:VALUES 'john', 'mary', 'paul';

Dalla versione 3.7.11 SQLite fa supporto multi-row-insert . Commenti di Richard Hipp:

"Il nuovo inserto multivalore è semplicemente suger sintattico (sic) per l'inserto composto. Non vi è alcun vantaggio in termini di prestazioni in un modo o nell'altro."


Non vi è alcun vantaggio in termini di prestazioni in un modo o nell'altro. - Potresti dirmi dove hai visto quell'osservazione? Non sono riuscito a trovarlo da nessuna parte.
Alix Axel,

15

A partire dalla versione 2012-03-20 (3.7.11), sqlite supporta la seguente sintassi INSERT:

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

Leggi la documentazione: http://www.sqlite.org/lang_insert.html

PS: per favore fai +1 sulla risposta / risposta di Brian Campbell. non mio! Prima ha presentato la soluzione.


10

Come hanno detto gli altri poster, SQLite non supporta questa sintassi. Non so se INSERT composti sono parte dello standard SQL, ma nella mia esperienza che stanno non implementato in molti prodotti.

A parte questo, dovresti essere consapevole che le prestazioni INSERT in SQLite sono notevolmente migliorate se includi più INSERT in una transazione esplicita.


10

Sì, sql può farlo, ma con una sintassi diversa. La documentazione di sqlite è piuttosto buona, comunque. Essa vi dirà anche che l'unico modo per inserire diversi riga è utilizzare un'istruzione select da essere inserita la fonte dei dati.


9

Sqlite3 non può farlo direttamente in SQL se non tramite un SELECT, e mentre SELECT può restituire una "riga" di espressioni, non conosco alcun modo per farlo restituire una colonna fasulla.

Tuttavia, la CLI può farlo:

.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 si inserisce un ciclo attorno a un INSERT, anziché utilizzare il .importcomando CLI , assicurarsi di seguire i consigli nelle FAQ di sqlite per la velocità INSERT:

Per impostazione predefinita, ogni istruzione INSERT è la propria transazione. Ma se si circondano più istruzioni INSERT con BEGIN ... COMMIT, tutti gli inserti vengono raggruppati in un'unica transazione. Il tempo necessario per eseguire il commit della transazione viene ammortizzato su tutte le istruzioni insert allegate e quindi il tempo per istruzione insert è notevolmente ridotto.

Un'altra opzione è eseguire PRAGMA sincrono = OFF. Questo comando farà in modo che SQLite non attenda che i dati raggiungano la superficie del disco, il che farà apparire le operazioni di scrittura molto più veloci. Ma se si perde potenza nel mezzo di una transazione, il file del database potrebbe essere danneggiato.


Se guardi il codice sorgente per il .importcomando di SQLite , è solo un ciclo, che legge una riga dal file di input (o tty) e quindi un'istruzione INSERT per quella riga. Purtroppo non ha migliorato significativamente l'efficienza.
Bill Karwin,

8

Alex ha ragione: l'istruzione "seleziona ... unione" perderà l'ordinamento che è molto importante per alcuni utenti. Anche quando si inserisce in un ordine specifico, sqlite cambia le cose, quindi preferire utilizzare le transazioni se è importante inserire l'ordine.

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

8

fearless_fool ha un'ottima risposta per le versioni precedenti. Volevo solo aggiungere che è necessario assicurarsi di avere tutte le colonne elencate. Quindi, se hai 3 colonne, devi assicurarti che la selezione agisca su 3 colonne.

Esempio: ho 3 colonne ma voglio solo inserire 2 colonne di dati. Supponiamo che non mi interessi della prima colonna perché è un ID intero standard. Potrei fare quanto segue ...

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: Ricorda che l'istruzione "seleziona ... unione" perderà l'ordine. (Da AG1)


7
INSERT INTO TABLE_NAME 
            (DATA1, 
             DATA2) 
VALUES      (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2), 
            (VAL1, 
             VAL2); 

6

Non puoi ma non credo che ti manchi nulla.

Poiché si chiama sqlite sempre in elaborazione, quasi non importa in termini di prestazioni se si esegue 1 istruzione insert o 100 istruzioni insert. Tuttavia, il commit richiede molto tempo, quindi inserisci quei 100 inserti all'interno di una transazione.

Sqlite è molto più veloce quando si utilizzano query con parametri (molto meno analisi necessarie) quindi non concatenerei grandi affermazioni come questa:

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

Devono essere analizzati più e più volte perché ogni affermazione concatenata è diversa.


6

in mysql lite non è possibile inserire più valori, ma è possibile risparmiare tempo aprendo la connessione solo una volta e quindi facendo tutti gli inserimenti e quindi chiudendo la connessione. Risparmia molto tempo


5

Il problema con l'utilizzo della transazione è che si blocca anche la tabella per la lettura. Quindi, se hai davvero molti dati da inserire e devi accedere ai tuoi dati, ad esempio un'anteprima o giù di lì, in questo modo non funziona bene.

Il problema con l'altra soluzione è che si perde l'ordine dell'inserimento

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

In sqlite i dati verranno archiviati a, b, c, d ...


5

A partire dalla versione 3.7.11 SQLite supporta l'inserimento multi-riga. Commenti di Richard Hipp:

Sto usando 3.6.13

Comando in questo modo:

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

Con 50 record inseriti alla volta, ci vuole solo un secondo o meno.

È vero che utilizzare sqlite per inserire più righe alla volta è molto possibile. Di @Andy ha scritto.

grazie Andy +1


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

2

Se si utilizza il plug-in firefox del gestore Sqlite , supporta inserimenti di massa dalle INSERTistruzioni SQL.

Infatti non supporta questo, ma Sqlite Browser funziona (funziona su Windows, OS X, Linux)


2

Ho una domanda come di seguito, ma con il driver ODBC SQLite ha un errore con "," dice. Corro vbscript in HTA (Applicazione 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())

2

Su sqlite 3.7.2:

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

e così via


2

Sono in grado di rendere dinamica la query. Questo è il mio tavolo:

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

e sto ricevendo tutti i dati attraverso un JSON, quindi dopo aver inserito tutto all'interno di un NSArrayho seguito questo:

    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 infine la query di output è questa:

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')

che funziona bene anche attraverso il codice e sono in grado di salvare con successo tutto in SQLite.

Prima di questo ho reso UNIONdinamico il roba delle query, ma questo ha iniziato a dare qualche errore di sintassi. Comunque, questo sta funzionando bene per me.


2

Sono sorpreso che nessuno abbia menzionato dichiarazioni preparate . A meno che tu non stia usando SQL da solo e non in qualsiasi altra lingua, penso che le istruzioni preparate racchiuse in una transazione sarebbero il modo più efficiente di inserire più righe.


1
Le dichiarazioni preparate sono sempre una buona idea, ma non sono affatto correlate alla domanda che l'OP sta ponendo. Sta chiedendo qual è la sintassi di base per l'inserimento di più dati in un'istruzione.
Lakey,


-1

Se stai usando bash shell puoi usare questo:

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 se sei nella CLI di sqlite, devi fare questo:

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;

Come funziona? Lo utilizza se table tab:

id int
------
1
2

quindi select a.id, b.id from tab a, tab britorna

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

e così via. Dopo la prima esecuzione inseriamo 2 righe, quindi 2 ^ 3 = 8. (tre perché abbiamotab a, tab b, tab c )

Dopo la seconda esecuzione inseriamo ulteriori (2+8)^3=1000 righe

Dopo tre inseriamo delle max(1000^3, 5e5)=500000righe e così via ...

Questo è il metodo più conosciuto per me per popolare il database SQLite.


2
Questo non funziona se si desidera inserire dati utili .
CL.

@CL. non è vero. puoi mescolarlo con date e ID casuali come preferisci.
test30
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.