web-dev-qa-db-ja.com

sqlite3の一括挿入が高速ですか?

Sqlite3データベースにロードしたい約30000行のデータのファイルがあります。データの各行に対して挿入ステートメントを生成するよりも速い方法はありますか?

データはスペースで区切られ、sqlite3テーブルに直接マップされます。ボリュームデータをデータベースに追加するための一括挿入方法はありますか?

組み込まれていない場合、誰かがこれを行うための明らかに素晴らしい方法を考案しましたか?

私はこれを前もって尋ねるべきです、APIからそれを行うC++の方法はありますか?

42
scubabbl

いくつかのパラメーターを調整する を試して、速度をさらに上げることもできます。具体的には、おそらくPRAGMA synchronous = OFF;

21
user42092
  • 単一のユーザーがいる場合でも、トランザクションですべてのINSERTをラップしますが、はるかに高速です。
  • 準備されたステートメントを使用します。
57
Javier

.importコマンドを使用したい。例えば:

$ cat demotab.txt
44      92
35      94
43      94
195     49
66      28
135     93
135     91
67      84
135     94

$ echo "create table mytable (col1 int, col2 int);" | sqlite3 foo.sqlite
$ echo ".import demotab.txt mytable"  | sqlite3 foo.sqlite

$ sqlite3 foo.sqlite
-- Loading resources from /Users/ramanujan/.sqliterc
SQLite version 3.6.6.2
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> select * from mytable;
col1    col2
44      92
35      94
43      94
195     49
66      28
135     93
135     91
67      84
135     94

この一括読み込みコマンドはSQLではなく、SQLiteのカスタム機能であることに注意してください。そのため、echoを介してインタラクティブなコマンドラインインタープリターであるsqlite3に渡すため、奇妙な構文になります。

PostgreSQLでは、同等のものはCOPY FROMhttp://www.postgresql.org/docs/8.1/static/sql-copy.html

MySQLではLOAD DATA LOCAL INFILEhttp://dev.mysql.com/doc/refman/5.1/en/load-data.html

最後に、.separatorの値に注意してください。これは、一括挿入を行うときによくある問題です。

sqlite> .show .separator
     echo: off
  explain: off
  headers: on
     mode: list
nullvalue: ""
   output: stdout
separator: "\t"
    width:

.importを実行する前に、セパレータをスペース、タブ、またはカンマに明示的に設定する必要があります。

35
ramanujan
  • 増加する PRAGMA default_cache_sizeより大きな数に。これにより、メモリにキャッシュされるページ数が増加します。

  • 行ごとに1つのトランザクションではなく、1つのトランザクションにすべての挿入をラップします。

  • コンパイルされたSQLステートメントを使用して挿入を行います。
  • 最後に、すでに述べたように、完全なACIDコンプライアンスをやめる場合は、PRAGMA synchronous = OFF;
18
paxos1977

RE:「データの各行に対して挿入ステートメントを生成するより速い方法はありますか?」

まず、Sqlite3の 仮想テーブルAPI を利用して、SQLステートメントを2つに減らします。

create virtual table vtYourDataset using yourModule;
-- Bulk insert
insert into yourTargetTable (x, y, z)
select x, y, z from vtYourDataset;

ここでの考え方は、ソースデータセットを読み取り、仮想テーブルとしてSQliteに提示するCインターフェイスを実装し、ソースからターゲットテーブルへのSQLコピーを一度に実行することです。それは実際よりも難しいように聞こえ、私はこの方法で大幅な速度の向上を測定しました。

次に、ここに記載されている他のアドバイス、つまりプラグマ設定とトランザクションを利用します。

3番目:おそらく、ターゲットテーブルのインデックスの一部を廃止できるかどうかを確認します。そうすれば、sqliteは挿入された各行に対して更新するインデックスが少なくなります

10
Hannes de Jager

一括挿入する方法はありませんが、大きなチャンクをメモリに書き込んでからデータベースにコミットする方法があります。 C/C++ APIの場合は、次のようにします。

sqlite3_exec(db、 "BEGIN TRANSACTION"、NULL、NULL、NULL);

...(INSERTステートメント)

sqlite3_exec(db、 "COMMIT TRANSACTION"、NULL、NULL、NULL);

Dbがデータベースポインターであると仮定します。

5
scott

良い妥協案は、BEGINSの間でINSERTをラップすることです。そして終わり;キーワード、つまり:

BEGIN;
INSERT INTO table VALUES ();
INSERT INTO table VALUES ();
...
END;
3
Flavien Volken

データのサイズと利用可能なRAMの量)に応じて、ディスクに書き込むのではなくオールインメモリデータベースを使用するようにsqliteを設定することにより、最高のパフォーマンスが得られます。

インメモリデータベースの場合、ファイル名引数としてNULLをsqlite3_openに渡し、 TEMP_STOREが適切に定義されていることを確認してください

(上記のテキストはすべて、 別のsqlite関連の質問 に対する自分の回答からの抜粋です)

3
pestophagous

私はいくつかの pragmas をテストしましたが、ここの回答で提案されています:

  • synchronous = OFF
  • journal_mode = WAL
  • journal_mode = OFF
  • locking_mode = EXCLUSIVE
  • synchronous = OFF + locking_mode = EXCLUSIVE + journal_mode = OFF

これは、トランザクションの挿入数が異なる場合の私の番号です。

バッチサイズを大きくすると、実際のパフォーマンスが向上しますが、ジャーナル、同期をオフにすると、排他ロックを取得してもわずかな利益が得られます。 〜110k前後のポイントは、ランダムなバックグラウンドロードがデータベースのパフォーマンスにどのように影響するかを示しています。

また、journal_mode=WALはデフォルトの代わりに使用できるので注意してください。ある程度の利益は得られますが、信頼性は低下しません。

C#コード。

3
astef

これは、1回限りのインポートに適したミックスであることがわかりました。

.echo ON

.read create_table_without_pk.sql

PRAGMA cache_size = 400000; PRAGMA synchronous = OFF; PRAGMA journal_mode = OFF; PRAGMA locking_mode = EXCLUSIVE; PRAGMA count_changes = OFF; PRAGMA temp_store = MEMORY; PRAGMA auto_vacuum = NONE;

.separator "\t" .import a_tab_seprated_table.txt mytable

BEGIN; .read add_indexes.sql COMMIT;

.exit

ソース: http://erictheturtle.blogspot.be/2009/05/fastest-bulk-import-into-sqlite.html

いくつかの追加情報: http://blog.quibb.org/2010/08/fast-bulk-inserts-into-sqlite/

1
maazza

あなたが一度だけ挿入しているなら、私はあなたのために汚いトリックを持っているかもしれません。

アイデアは単純で、最初にメモリデータベースに挿入し、次にバックアップして、最後に元のデータベースファイルに復元します。

詳細な手順は my blog で書きました。 :)

0
circle