web-dev-qa-db-ja.com

Python CSV to SQLite

大きな(〜1.6GB)CSVファイルを「変換」し、CSVの特定のフィールドをSQLiteデータベースに挿入しています。基本的に私のコードは次のようになります:

import csv, sqlite3

conn = sqlite3.connect( "path/to/file.db" )
conn.text_factory = str  #bugger 8-bit bytestrings
cur = conn.cur()
cur.execute('CREATE TABLE IF NOT EXISTS mytable (field2 VARCHAR, field4 VARCHAR)')

reader = csv.reader(open(filecsv.txt, "rb"))
for field1, field2, field3, field4, field5 in reader:
  cur.execute('INSERT OR IGNORE INTO mytable (field2, field4) VALUES (?,?)', (field2, field4))

例外はありますが、すべてが期待どおりに機能します。処理に非常に多くの時間がかかります。間違ってコーディングしていますか?より高いパフォーマンスを達成し、必要なものを達成するためのより良い方法はありますか(CSVのいくつかのフィールドをSQLiteテーブルに単純に変換する)?

**編集-推奨どおりcsvをsqliteに直接インポートしようとしましたが、ファイルにフィールドにカンマが含まれていることがわかりました(例:"My title, comma")。インポートでエラーが発生しています。それらのオカレンスが多すぎて手動でファイルを編集できないようです...

他の考え?? **

21
user735304

CSVを直接インポートすることは可能です:

sqlite> .separator ","
sqlite> .import filecsv.txt mytable

http://www.sqlite.org/cvstrac/wiki?p=ImportingFiles

26
fengb

Chrisが正しい-トランザクションを使用します。データをチャンクに分割して保存します。

"...トランザクション内にない限り、各SQLステートメントは新しいトランザクションを開始します。これは、それぞれのジャーナルファイルを再度開いたり、書き込んだり、閉じたりする必要があるため、非常にコストがかかります。ステートメント。これは、一連のSQLステートメントをBEGIN TRANSACTION;およびEND TRANSACTION;ステートメントでラップすることで回避できます。この高速化は、データベースを変更しないステートメントでも得られます。 "-ソース: http://web.utk.edu/~jplyon/sqlite/SQLite_optimization_FAQ.html

"... SQLiteを高速化するために使用できる別のトリックがあります。トランザクションです。複数のデータベース書き込みを行う必要がある場合は、トランザクション内に配置してください。書き込み(およびロック)する代わりに書き込みクエリが発行されるたびに、ファイルはトランザクションが完了したときに一度だけ発生します。 "-ソース: SQLiteのスケーラブルな方法

import csv, sqlite3, time

def chunks(data, rows=10000):
    """ Divides the data into 10000 rows each """

    for i in xrange(0, len(data), rows):
        yield data[i:i+rows]


if __name__ == "__main__":

    t = time.time()

    conn = sqlite3.connect( "path/to/file.db" )
    conn.text_factory = str  #bugger 8-bit bytestrings
    cur = conn.cur()
    cur.execute('CREATE TABLE IF NOT EXISTS mytable (field2 VARCHAR, field4 VARCHAR)')

    csvData = csv.reader(open(filecsv.txt, "rb"))

    divData = chunks(csvData) # divide into 10000 rows each

    for chunk in divData:
        cur.execute('BEGIN TRANSACTION')

        for field1, field2, field3, field4, field5 in chunk:
            cur.execute('INSERT OR IGNORE INTO mytable (field2, field4) VALUES (?,?)', (field2, field4))

        cur.execute('COMMIT')

    print "\n Time Taken: %.3f sec" % (time.time()-t) 
23
Sam

言われているように(ChrisとSam)、トランザクションは多くの挿入パフォーマンスを向上させます。

Pythonユーティリティを使用してCSVを操作する csvkit を使用するには、別のオプションをお勧めします。

インストールするには:

pip install csvkit

あなたの問題を解決するために

csvsql --db sqlite:///path/to/file.db --insert --table mytable filecsv.txt
17
migonzalvar

トランザクションを使用してみてください。

begin    
insert 50,000 rows    
commit

行ごとに1回ではなく、定期的にデータをコミットします。

3
Chris N