SQLiteデータベースに一度に複数の行を挿入することは可能ですか?


551

MySQLでは、次のように複数の行を挿入できます。

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

しかし、このようなことをしようとするとエラーが発生します。SQLiteデータベースに一度に複数の行を挿入することは可能ですか?それを行うための構文は何ですか?


26
この質問は、一般的なSQLとは対照的に、SQLiteについて具体的に尋ねているためです。
ブライアンキャンベル、

5
バルクインサート上:stackoverflow.com/questions/1711631/...
tuinstoel

5
問題は、なぜそうすべきなのかです。SQliteは同じマシン上でインプロセスであり、トランザクションに複数の挿入をラップできるので、必要はないと思いますか?
andig

2
はい、バージョン2012-03-20(3.7.11)から開始してください。構文はサポートされています。
mjb 2013年

回答:


607

更新

以下のようBrianCampbellはここで指摘して上記のSQLite 3.7.11とは現在、オリジナルのポストの簡単な構文をサポートしています。ただし、レガシーデータベース間で最大の互換性が必要な場合は、ここに示すアプローチが適切です。

元の答え

私が権限を持っていた場合、私はバンプう川の返信を:あなたができる SQLiteの中で複数の行を挿入し、あなただけの必要な別の構文を。それを完全に明確にするために、OPs MySQLの例:

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

これは次のようにSQLiteに再キャストできます。

     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'

パフォーマンスに関するメモ

私はもともとこの手法を使用して、Ruby on Railsから大きなデータセットを効率的にロードしました。 ただしJaime Cookが指摘しているように、これがINSERTs単一のトランザクション内で個人をより速くラッピングすることは明らかではありません。

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

効率が目標の場合は、最初にこれを試す必要があります。

UNIONとUNION ALLに関するメモ

複数のユーザーがコメントしたように、UNION ALL(上記のように)を使用すると、すべての行が挿入されるため、この場合は4行のになりdata1, data2ます。を省略するALLと、重複する行が削除されます(おそらく操作が少し遅くなります)。元の投稿のセマンティクスにより近いため、UNION ALLを使用しています。

最後に

PS:最初に解決策を提示したので川の返信を +1してください。


102
さらに注意点としては、sqliteの唯一のあなたは500個の要素ブロック(にそれを分割する必要があります。それよりも多くのデータにスローするようにしようとしているので、もしクエリあたり500のなど労働組合の選択件までサポートしているようだsqlite.org/limits.html
ジェイミー・クック

3
同意:SQLiteはボトルネックではなく、個々のORMトランザクション(私の場合はRuby On Rails)のオーバーヘッドです。ですから、それはまだ大きな勝利です。
fearless_fool

3
このソリューションまたは3.7.11ソリューションは、トランザクションブロックを使用する場合とどのように違いますか?それは速いにありますinsert into t values (data),(data),(data)begin transaction; insert into t values (data);insert into t values (data);insert into t values (data);Commit;
ダン

5
さらに注意してください:注意してください!この構文は重複する行を削除します!それUNION ALLを回避するために使用します(またはいくつかのマイナーなパフォーマンスの向上のため)。
chacham15 14年

1
@ShadowfaxでSQLiteのバージョンを確認しますsqlite3.sqlite_version(これは3.7.xまたは3.8.xである必要があります)ではなくsqlite3.version、Pythonモジュールのバージョンです。
最大

562

はい、可能ですが、通常のコンマ区切りの挿入値ではできません。

これを試して...

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

はい、それは少し醜いですが、一連の値からステートメントの生成を自動化するのは簡単です。また、最初の選択で列名を宣言するだけでよいようです。


36
使用しUNION ALLないでくださいUNION。重複を削除する場合を除きます。
ブノワ

4
IDを自動インクリメントする場合は、NULL値を指定します。
lenooh

241

はい、SQLite 3.7.11以降、これはSQLiteでサポートされています。SQLiteのドキュメントから:

SQLite INSERTステートメントの構文

(この回答が最初に書かれたとき、これはサポートされていませんでした)

SQLiteの古いバージョンとの互換性のために、あなたは、によって提案されたトリックを使用することができアンディfearless_fool使用しUNIONますが、3.7.11以降では、ここで説明した簡単な構文が好まれるべきです。


3
の後にかっこの外側にコンマがある閉じたループがあるので、ダイアグラムは複数の行を許可すると思いますVALUES
Johannes Gerer 2012年

@JohannesGerer私が埋め込んだ後、彼らはこの画像を更新しました。この図は、インターネットアーカイブに埋め込んだ時点でご覧いただけます。実際、挿入で複数行のサポートを追加したのはわずか2か月前で、その変更を含むバージョンをリリースしたのはわずか2日前です。それに応じて回答を更新します。
ブライアンキャンベル

1
@Brian、あなたはこれがあることと述べ、SQLiteのドキュメントのテキストを引用してください可能性がIS可能?私は挿入ドキュメントを3回再読み込みしましたが、複数の行の挿入については何も見つかりませんでしたが、この画像のみ(およびのカンマについては何もありませんVALUES (...), (...)):(
Prizoff

1
@Prizoff テストケースを含め、このサポートが追加されたコミットにリンクしまし。図(IAリンクと比較)を見ると、式の後にループがあり、VALUESコンマで区切って繰り返すことができることがわかります。また、機能を追加したバージョンのリリースノートにリンクしました。これには、「VALUES句を使用して複数の行を挿入できるようにINSERT構文を強化する」と記載されています。
ブライアンキャンベル

1
@Prizoff SQLiteのメンテナにこれについて言及しました。彼はドラフトドキュメントで利用可能な修正コミットしました。それは次のリリースの時点で公式のドキュメントになると思います。
ブライアンキャンベル

57

一連の挿入ステートメントから単一の500要素の複数行挿入を生成するために、個々の挿入を実行するよりもかなり高速なルビコードをいくつか作成しました。次に、複数の挿入を1つのトランザクションに単純にラップしてみたところ、かなり少ないコードで同じ種類の速度を上げることができることがわかりました。

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

1
しかし、SQLiteマネージャーで直接機能している間は、コードでは機能しませんでした。コードでは、1行目のみを挿入します:(
Vaibhav Saran

4
私はコードでこのスタイルの挿入を使用しており、完全に機能します。一度にすべてのSQLを送信するようにしてください。これは私にとっては大幅な速度の増加でしたが、受け入れられた回答がこれより速いか遅いか知りたいです。
Dan

このアプローチは、単一のトランザクション内で複数のテーブルを変更するときに非常によく拡張されます。私は、データベースをバッチロードするときによく行っています。
Frank

バインディングを使用する必要がある場合、このアプローチは機能しないことに注意してください。SQLite3エンジンは、すべてのバインディングが最初のステートメントに適用されると想定し、次のステートメントを無視します。より詳細な説明については、このSOの質問を参照してください。
フィリップエベール2018年

38

このページによるとそれはサポートされていません:

  • 2007-12-03:複数行INSERT別名複合INSERTはサポートされていません。
  INSERT INTO table (col1, col2) VALUES 
      ('row1col1', 'row1col2'), ('row2col1', 'row2col2'), ...

実際、SQL92標準によれば、VALUES式はそれ自体で立つことができるはずです。たとえば、次の例では、3つの行を持つ1列のテーブルが返されます。VALUES 'john', 'mary', 'paul';

バージョン3.7.11以降、SQLite 複数行挿入をサポートしています。Richard Hippのコメント:

「新しい多値挿入は、複合挿入の単なる構文糖(sic)です。どちらにしてもパフォーマンス上の利点はありません。」


どちらにしてもパフォーマンス上の利点はありません。-その発言をどこで見ましたか?どこにも見つかりませんでした。
Alix Axel 2013年

15

バージョン2012-03-20(3.7.11)以降、sqliteは次のINSERT構文をサポートします。

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

ドキュメントを読む:http : //www.sqlite.org/lang_insert.html

PS:ブライアンキャンベルの返信/回答に+1してください。私のじゃない!彼は最初に解決策を提示しました。


10

他の投稿者が述べたように、SQLiteはこの構文をサポートしていません。複合INSERTがSQL標準の一部であるかどうかはわかりませんが、私の経験では、それらは多くの製品に実装されていません

余談ですが、明示的なトランザクションで複数のINSERTをラップすると、SQLiteのINSERTパフォーマンスが大幅に向上することに注意してください。



9

Sqlite3はSELECTを介して以外はSQLで直接それを行うことはできません。SELECTは式の「行」を返すことができますが、偽の列を返すようにする方法はありません。

ただし、CLIはそれを行うことができます。

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

CLI .importコマンドを使用するのではなく、INSERTの周りにループを配置する場合は、SQLite FAQのINSERT速度に関するアドバイスに従ってください。

デフォルトでは、各INSERTステートメントは独自のトランザクションです。ただし、BEGIN ... COMMITで複数のINSERTステートメントを囲むと、すべての挿入が1つのトランザクションにグループ化されます。トランザクションをコミットするのに必要な時間は、含まれているすべての挿入ステートメントにわたって償却されるため、挿入ステートメントごとの時間は大幅に短縮されます。

別のオプションは、PRAGMA Synchronization = OFFを実行することです。このコマンドは、SQLiteがデータがディスク表面に到達するのを待たないようにするため、書き込み操作がはるかに高速に見えるようになります。ただし、トランザクションの途中で電源が切れると、データベースファイルが破損する可能性があります。


SQLiteの.importコマンドのソースコードを見ると、それは単なるループであり、入力ファイル(またはtty)から行を読み取り、その行のINSERTステートメントを読み取っています。残念ながら、効率は大幅には改善されていません。
Bill Karwin

8

Alexは正解です。「select ... union」ステートメントは、一部のユーザーにとって非常に重要な順序を失います。特定の順序で挿入する場合でも、sqliteは状況を変更するため、挿入の順序が重要な場合はトランザクションを使用することをお勧めします。

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は、古いバージョンに対して素晴らしい答えを持っています。すべての列がリストされていることを確認する必要があることを追加したいと思います。したがって、3つの列がある場合、selectが3つの列に作用することを確認する必要があります。

例:3列ありますが、2列分のデータのみを挿入します。最初の列は標準の整数IDなので、気にしないと仮定します。私は次のことができました...

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'

注:「select ... union」ステートメントは順序を失うことに注意してください。(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

あなたはそうすることはできませんが、私はあなたが何かを見逃しているとは思いません。

sqliteは常に処理中に呼び出されるため、1つの挿入ステートメントを実行しても100の挿入ステートメントを実行しても、パフォーマンスにはほとんど関係ありません。ただし、コミットには時間がかかるため、これらの100個の挿入をトランザクション内に配置します。

パラメーター化されたクエリを使用すると、Sqliteがはるかに高速になるため(必要な解析がはるかに少なくなります)、次のような大きなステートメントを連結しません。

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

すべての連結されたステートメントは異なるため、それらは何度も解析する必要があります。


6

mysql liteでは、複数の値を挿入することはできませんが、接続を1回だけ開いてから、すべての挿入を行ってから接続を閉じることで、時間を節約できます。それは多くの時間を節約します


5

トランザクションを使用する場合の問題は、読み取りのためにもテーブルをロックすることです。そのため、挿入するデータが本当に多く、データにアクセスする必要がある場合(たとえば、プレビューなど)、この方法はうまく機能しません。

他のソリューションの問題は、挿入の順序を失うことです

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

sqliteでは、データはa、b、c、dに格納されます...


5

バージョン3.7.11以降、SQLiteは複数行挿入をサポートしています。Richard Hippのコメント:

3.6.13を使用しています

私はこのようにコマンドします:

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

一度に50レコードが挿入されるので、1秒以下しかかかりません。

sqliteを使用して一度に複数の行を挿入することは可能です。@Andyが書いた。

アンディ+1に感謝


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


2

以下のようなクエリがありますが、ODBCドライバーを使用すると、SQLiteで「」というエラーが発生します。HTA(Html Application)でvbscriptを実行します。

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

sqlite 3.7.2の場合:

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

等々


2

クエリを動的にすることができます。これは私のテーブルです:

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

そして、私はすべてのデータをを通して取得しているJSONので、内部にすべてを取得した後、NSArray私はこれに従いました:

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

そして最後に、出力クエリはこれです:

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

コードでも問題なく実行されており、SQLiteにすべてを正常に保存できます。

これの前に、UNIONクエリを動的に作成しましたが、構文エラーが発生し始めました。とにかく、これは私にとってうまくいきます。


2

誰もが準備されたステートメントについて言及していないことに驚いています。SQLを単独で使用し、他の言語では使用しない場合を除き、トランザクションにラップされた準備済みステートメントが複数の行を挿入する最も効率的な方法だと思います。


1
準備されたステートメントは常に良いアイデアですが、OPが求めている質問とはまったく関係ありません。彼は、1つのステートメントに複数のデータを挿入するための基本的な構文を尋ねています。
Lakey


-1

bashシェルを使用している場合は、これを使用できます。

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

または、sqlite CLIを使用している場合は、次のようにする必要があります。

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;

どのように機能しますか?これはif tableを利用しますtab

id int
------
1
2

次にselect a.id, b.id from tab a, tab b戻ります

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

等々。最初の実行後、2つの行を挿入し、次に2 ^ 3 = 8を挿入します。(3つあるのでtab a, tab b, tab c

2回目の実行後、追加の(2+8)^3=1000行を挿入します

その後、max(1000^3, 5e5)=500000行などを挿入します...

これは、SQLiteデータベースにデータを入力する方法としては、私にとって最も速い方法です。


2
有用なデータを挿入したい場合、これは機能しません。
CL。

@CL。それは真実ではない。ランダムな日付とIDを自由に組み合わせることができます。
test30 14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.