Est-il possible d'insérer plusieurs lignes à la fois dans une base de données SQLite?

551

Dans MySQL, vous pouvez insérer plusieurs lignes comme ceci:

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

Cependant, je reçois une erreur lorsque j'essaie de faire quelque chose comme ça. Est-il possible d'insérer plusieurs lignes à la fois dans une base de données SQLite? Quelle est la syntaxe pour faire ça?

Andrew
la source
26
Pas dupe, car cette question concerne spécifiquement SQLite, par opposition à SQL en général (bien que certaines des réponses à cette question soient utiles pour celle-ci).
Brian Campbell,
5
Sur les insertions en vrac: stackoverflow.com/questions/1711631/…
tuinstoel
5
La question est de savoir pourquoi vous devriez le faire. Comme SQlite est en cours sur la même machine et que vous pouvez encapsuler plusieurs insertions dans une transaction, je ne vois pas la nécessité?
andig
2
Oui, à partir de la version 2012-03-20 (3.7.11), votre syntaxe est prise en charge.
mjb

Réponses:

607

mise à jour

Comme le souligne BrianCampbell ici , SQLite 3.7.11 et supérieur prend désormais en charge la syntaxe plus simple de la publication d'origine . Cependant, l'approche présentée est toujours appropriée si vous souhaitez une compatibilité maximale entre les bases de données héritées.

réponse originale

Si j'avais des privilèges, je répondrais à la réponse de River : vous pouvez insérer plusieurs lignes dans SQLite, vous avez juste besoin d' une syntaxe différente . Pour être parfaitement clair, l'exemple OPs MySQL:

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

Cela peut être refondu dans SQLite comme:

     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'

une note sur la performance

À l'origine, j'ai utilisé cette technique pour charger efficacement de grands ensembles de données à partir de Ruby on Rails. Cependant , comme le souligne Jaime Cook , il n'est pas clair qu'il s'agit d'un individu plus rapide INSERTsdans une seule transaction:

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

Si l'efficacité est votre objectif, essayez-le d'abord.

une note sur UNION vs UNION ALL

Comme plusieurs personnes l'ont commenté, si vous utilisez UNION ALL(comme indiqué ci-dessus), toutes les lignes seront insérées, donc dans ce cas, vous obtiendrez quatre lignes de data1, data2. Si vous omettez la ALL, les lignes en double seront éliminées (et l'opération sera probablement un peu plus lente). Nous utilisons UNION ALL car il correspond plus étroitement à la sémantique du message d'origine.

en terminant

PS: Veuillez attribuer +1 à la réponse de river , car elle a présenté la solution en premier.

intrépide_fool
la source
102
En outre, sqlite ne semble prendre en charge jusqu'à 500 sélections d'union par requête, donc si vous essayez d'ajouter plus de données que cela, vous devrez les diviser en 500 blocs d'éléments ( sqlite.org/limits.html )
Jamie Cook
3
D'accord: SQLite n'est pas le goulot d'étranglement, c'est la surcharge des transactions ORM individuelles (dans mon cas, Ruby On Rails). C'est donc toujours une grosse victoire.
fearless_fool
3
Comment cette solution ou la solution 3.7.11 se compare-t-elle à l'utilisation de blocs de transaction? est-ce plus rapide pour 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;?
Dan
5
Autre remarque: soyez prudent! cette syntaxe supprime les lignes en double! utiliser UNION ALLpour éviter cela (ou pour certains gains de performance mineurs).
chacham15
1
@Shadowfax pour vérifier la version de SQLite, regardez sqlite3.sqlite_version(ce devrait être 3.7.x ou 3.8.x), pas sqlite3.version(qui est juste la version du module python).
max
562

Oui, c'est possible, mais pas avec les valeurs d'insertion habituelles séparées par des virgules.

Essaye ça...

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

Oui, c'est un peu moche mais assez facile pour automatiser la génération de l'instruction à partir d'un ensemble de valeurs. En outre, il semble que vous ayez seulement besoin de déclarer les noms de colonne lors de la première sélection.

rivière
la source
36
veuillez UNION ALLne pas utiliser UNION. Sauf si vous souhaitez supprimer les doublons.
Benoit
4
Si vous souhaitez que les ID s'incrémentent automatiquement, donnez-leur une valeur NULL.
lenooh
241

Oui, depuis SQLite 3.7.11, cela est pris en charge dans SQLite. De la documentation SQLite :

Syntaxe de l'instruction SQLite INSERT

(lorsque cette réponse a été écrite à l'origine, cela n'était pas pris en charge)

Pour la compatibilité avec les anciennes versions de SQLite, vous pouvez utiliser l'astuce suggérée par andy et fearless_fool using UNION, mais pour 3.7.11 et versions ultérieures, la syntaxe plus simple décrite ici devrait être préférée.

Brian Campbell
la source
3
Je dirais que le diagramme autorise plusieurs lignes, car il y a une boucle fermée avec une virgule en dehors des parenthèses après VALUES.
Johannes Gerer
@JohannesGerer Ils ont mis à jour cette image depuis que je l'ai incorporée. Vous pouvez voir le diagramme au moment où je l'ai intégré aux archives Internet . En fait, il y a tout juste deux mois, ils ont ajouté la prise en charge de plusieurs lignes dans un encart, et il y a tout juste deux jours, ils ont publié une version avec ce changement. Je mettrai à jour ma réponse en conséquence.
Brian Campbell
1
@ Brian, pourriez - vous s'il vous plaît cites texte de documentation SQLite, qui indique que c'EST possible? J'ai relu insert docs 3 fois et je n'ai rien trouvé sur l'insertion de plusieurs lignes, mais seulement cette image (et rien sur la virgule VALUES (...), (...)) :(
Prizoff
1
@Prizoff J'ai lié au commit dans lequel ce support a été ajouté , y compris les cas de test. Vous pouvez voir dans le diagramme (comparer le lien IA ), qu'il y a une boucle autour de l'expression après VALUES, indiquant qu'elle peut être répétée séparée par des virgules. Et j'ai lié aux notes de publication de la version ajoutant la fonctionnalité, qui stipulent "Améliorez la syntaxe INSERT pour permettre l'insertion de plusieurs lignes via la clause VALUES".
Brian Campbell,
1
@Prizoff J'en ai parlé au mainteneur SQLite, il a validé un correctif qui est disponible dans la documentation provisoire . Je suppose que ce sera dans la documentation officielle à partir de la prochaine version.
Brian Campbell,
57

J'ai écrit du code ruby ​​pour générer un seul insert à plusieurs lignes de 500 éléments à partir d'une série d'instructions d'insertion qui était considérablement plus rapide que l'exécution des insertions individuelles. Ensuite, j'ai simplement essayé de regrouper les multiples insertions dans une seule transaction et j'ai constaté que je pouvais obtenir le même type d'accélération avec beaucoup moins de code.

BEGIN TRANSACTION;
INSERT INTO table VALUES (1,1,1,1);
INSERT INTO table VALUES (2,2,2,2);
...
COMMIT;
Jamie Cook
la source
1
mais cela n'a pas fonctionné dans le code, alors qu'il fonctionne directement dans le gestionnaire SQLite. Dans le code, il insère uniquement la 1ère ligne :(
Vaibhav Saran
4
J'utilise ce style d'insertion dans mon code et cela fonctionne parfaitement. Vous n'avez qu'à vous assurer de soumettre TOUS les SQL en une seule fois. Ce fut une augmentation de vitesse énorme pour moi, mais je suis curieux de savoir si la réponse acceptée est plus rapide ou plus lente alors?
Dan
Cette approche évolue très bien lors de la modification de plusieurs tables au sein d'une même transaction, ce que je fais souvent lors du chargement par lots d'une base de données.
Frank
Notez que cette approche ne fonctionnera pas si vous devez utiliser des liaisons . Le moteur SQLite3 supposera que toutes vos liaisons doivent être appliquées à la première instruction et ignorera les instructions suivantes. Voir cette question SO pour une explication plus détaillée.
Philippe Hebert
38

Selon cette page, il n'est pas pris en charge:

  • 2007-12-03: INSERT à plusieurs lignes, INSERT composé non pris en charge.
  INSERT INTO table (col1, col2) VALUES 
      ('row1col1', 'row1col2'), ('row2col1', 'row2col2'), ...

En fait, selon la norme SQL92, une expression VALUES devrait pouvoir se tenir sur elle-même. Par exemple, les éléments suivants doivent renvoyer un tableau à une colonne avec trois lignes:VALUES 'john', 'mary', 'paul';

Depuis la version 3.7.11, SQLite prend en charge l' insertion sur plusieurs lignes . Richard Hipp commente:

"Le nouvel insert à valeurs multiples est simplement un argument syntaxique (sic) pour l'insert composé. Il n'y a aucun avantage de performance dans un sens ou dans l'autre."

typeseven
la source
Il n'y a aucun avantage de performance dans un sens ou dans l'autre. - Pourriez-vous me dire où avez-vous vu cette remarque? Je ne l'ai trouvé nulle part.
Alix Axel
15

Depuis la version 2012-03-20 (3.7.11), sqlite prend en charge la syntaxe INSERT suivante:

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

Lisez la documentation: http://www.sqlite.org/lang_insert.html

PS: veuillez attribuer +1 à la réponse / réponse de Brian Campbell. pas le mien! Il a d'abord présenté la solution.

mjb
la source
10

Comme les autres affiches l'ont dit, SQLite ne prend pas en charge cette syntaxe. Je ne sais pas si les INSERT composés font partie de la norme SQL, mais d'après mon expérience, ils ne sont pas implémentés dans de nombreux produits.

En passant, vous devez savoir que les performances INSERT dans SQLite sont considérablement améliorées si vous encapsulez plusieurs INSERT dans une transaction explicite.

Larry Lustig
la source
10

Oui, sql peut le faire, mais avec une syntaxe différente. La documentation SQLite est assez bonne, par la manière. Il vous indiquera également que la seule façon d'insérer plusieurs lignes est d' utiliser une instruction select comme source des données à insérer.

innaM
la source
9

Sqlite3 ne peut pas faire cela directement en SQL, sauf via un SELECT, et bien que SELECT puisse renvoyer une "ligne" d'expressions, je ne connais aucun moyen de lui faire retourner une colonne bidon.

Cependant, la CLI peut le faire:

.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 vous mettez une boucle autour d'un INSERT, plutôt que d'utiliser la .importcommande CLI , assurez-vous de suivre les conseils de la FAQ sqlite pour la vitesse INSERT:

Par défaut, chaque instruction INSERT est sa propre transaction. Mais si vous entourez plusieurs instructions INSERT avec BEGIN ... COMMIT, toutes les insertions sont regroupées en une seule transaction. Le temps nécessaire pour valider la transaction est amorti sur toutes les instructions d'insertion jointes et le temps par instruction d'insertion est ainsi considérablement réduit.

Une autre option consiste à exécuter PRAGMA synchronous = OFF. Cette commande empêchera SQLite d'attendre que les données atteignent la surface du disque, ce qui rendra les opérations d'écriture beaucoup plus rapides. Mais si vous perdez de l'énergie au milieu d'une transaction, votre fichier de base de données peut devenir corrompu.

DigitalRoss
la source
Si vous regardez le code source de la .importcommande SQLite , c'est juste une boucle, lisant une ligne du fichier d'entrée (ou tty) puis une instruction INSERT pour cette ligne. Malheureusement, l'efficacité n'a pas été considérablement améliorée.
Bill Karwin
8

Alex a raison: l'instruction "select ... union" perdra la commande, ce qui est très important pour certains utilisateurs. Même lorsque vous insérez dans une commande spécifique, sqlite change les choses, alors préférez utiliser les transactions si la commande d'insertion est 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
la source
8

fearless_fool a une excellente réponse pour les anciennes versions. Je voulais juste ajouter que vous devez vous assurer que toutes les colonnes sont répertoriées. Donc, si vous avez 3 colonnes, vous devez vous assurer que la sélection agit sur 3 colonnes.

Exemple: J'ai 3 colonnes mais je veux seulement insérer 2 colonnes de données. Supposons que je ne me soucie pas de la première colonne, car il s'agit d'un identifiant d'entier standard. Je pourrais faire ce qui suit ...

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'

Remarque: N'oubliez pas que l'instruction "select ... union" perdra la commande. (De AG1)

LEO
la source
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
la source
6

Vous ne pouvez pas, mais je ne pense pas que vous manquez rien.

Parce que vous appelez sqlite toujours en cours, peu importe les performances, que vous exécutiez 1 instruction d'insertion ou 100 instructions d'insertion. Cependant, la validation prend beaucoup de temps, alors mettez ces 100 insertions dans une transaction.

Sqlite est beaucoup plus rapide lorsque vous utilisez des requêtes paramétrées (beaucoup moins d'analyse syntaxique nécessaire), donc je ne concatène pas de grandes instructions comme ceci:

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

Ils doivent être analysés encore et encore car chaque déclaration concaténée est différente.

tuinstoel
la source
6

dans mysql lite, vous ne pouvez pas insérer plusieurs valeurs, mais vous pouvez gagner du temps en ouvrant la connexion une seule fois, puis en faisant toutes les insertions et en fermant la connexion. Cela fait gagner beaucoup de temps

g.revolution
la source
5

Le problème avec l'utilisation de transaction est que vous verrouillez également la table pour la lecture. Donc, si vous avez vraiment beaucoup de données à insérer et que vous avez besoin d'accéder à vos données, par exemple un aperçu ou ainsi, cela ne fonctionne pas bien.

Le problème avec l'autre solution est que vous perdez l'ordre d'insertion

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

Dans le sqlite, les données seront stockées a, b, c, d ...

alex
la source
5

Depuis la version 3.7.11, SQLite prend en charge l'insertion sur plusieurs lignes. Richard Hipp commente:

J'utilise 3.6.13

Je commande comme ça:

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

Avec 50 enregistrements insérés à la fois, cela ne prend qu'une seconde ou moins.

Il est vrai que l'utilisation de sqlite pour insérer plusieurs lignes à la fois est très possible. Par @Andy a écrit.

merci Andy +1

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

Si vous utilisez le plug- in Sqlite manager firefox, il prend en charge les insertions en masse INSERTdes instructions SQL.

En fait, il ne prend pas en charge cela, mais le navigateur Sqlite le fait (fonctionne sur Windows, OS X, Linux)

Chris S
la source
2

J'ai une requête comme ci-dessous, mais avec le pilote ODBC, SQLite a une erreur avec "", dit-il. J'exécute vbscript dans HTA (Html ​​Application).

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
la source
2

Sur sqlite 3.7.2:

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

etc

ashakirov
la source
2

Je peux rendre la requête dynamique. Voici ma table:

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

et j'obtiens toutes les données via un JSON, donc après avoir tout mis à l'intérieur d'un, NSArrayj'ai suivi ceci:

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

Et enfin, la requête de sortie est la suivante:

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

qui fonctionne bien grâce au code également et je suis en mesure de tout enregistrer avec succès dans SQLite.

Avant cela, j'ai rendu les UNIONrequêtes dynamiques, mais cela a commencé à donner une erreur de syntaxe. Quoi qu'il en soit, cela fonctionne bien pour moi.

Vaibhav Saran
la source
2

Je suis surpris que personne n'ait mentionné de déclarations préparées . À moins que vous n'utilisiez SQL seul et dans aucun autre langage, je pense que les instructions préparées enveloppées dans une transaction seraient le moyen le plus efficace d'insérer plusieurs lignes.


la source
1
Les déclarations préparées sont toujours une bonne idée, mais ne sont pas du tout liées à la question que pose le PO. Il demande quelle est la syntaxe de base pour insérer plusieurs données dans une seule instruction.
Lakey
-1

Si vous utilisez le shell bash, vous pouvez utiliser ceci:

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 si vous êtes en sqlite CLI, alors vous devez faire ceci:

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;

Comment ça marche? Il utilise cette table if tab:

id int
------
1
2

puis select a.id, b.id from tab a, tab bretourne

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

etc. Après la première exécution, nous insérons 2 lignes, puis 2 ^ 3 = 8. (trois parce que nous avonstab a, tab b, tab c )

Après la deuxième exécution, nous insérons (2+8)^3=1000 lignes

Après trois, nous insérons environ max(1000^3, 5e5)=500000 lignes et ainsi de suite ...

C'est la méthode la plus rapide connue pour remplir la base de données SQLite.

test30
la source
2
Cela ne fonctionne pas si vous souhaitez insérer des données utiles .
CL.
@CL. ce n'est pas vrai. vous pouvez le mélanger avec des dates et des identités aléatoires comme vous le souhaitez.
test30