Pythonを使用してCSVファイルをsqlite3データベーステーブルにインポートする


105

CSVファイルがあり、Pythonを使用してこのファイルをsqlite3データベースに一括インポートしたい。コマンドは「.import .....」です。しかし、それはこのように機能することができないようです。sqlite3でそれを行う方法の例を誰かに教えてもらえますか?念のためwindowsを使っています。ありがとう


3
機能しなかった実際のコマンドと実際のエラーメッセージをお知らせください。「インポート...」は何でもかまいません。「働けない」は曖昧すぎて推測できません。詳細がなければ、私たちは助けることはできません。
S.Lott、

2
私が言った実際のコマンドは ".import"であり、構文エラーnew ".import"を示しています
Hossein

10
質問には実際のコマンドを実際に投稿してください。実際のエラーメッセージを質問に実際に投稿してください。単に内容を繰り返すコメントを追加しないでください。質問を実際のコピーと貼り付けで更新してください。
S.Lott

回答:


132
import csv, sqlite3

con = sqlite3.connect(":memory:") # change to 'sqlite:///your_filename.db'
cur = con.cursor()
cur.execute("CREATE TABLE t (col1, col2);") # use your column names here

with open('data.csv','r') as fin: # `with` statement available in 2.5+
    # csv.DictReader uses first line in file for column headings by default
    dr = csv.DictReader(fin) # comma is default delimiter
    to_db = [(i['col1'], i['col2']) for i in dr]

cur.executemany("INSERT INTO t (col1, col2) VALUES (?, ?);", to_db)
con.commit()
con.close()

4
私と同じ問題が発生した場合:col1とcol2をcsvファイルの列ヘッダーに変更してください。最後にcon.close()を呼び出して、データベースへの接続を閉じます。
ジョナス

1
ありがとう、@ Jonas。投稿を更新しました。
Mechanical_meat

私がnot all arguments converted during string formattingこの方法を試したときも、私は次々と手に入れました。
Whitecat

この方法を試しましたが、うまくいきません。ここで私のデータセットをチェックアウトして(それらは非常に正常ですが、一部の列には空の値があります)、コードでインポートしてみてください。stackoverflow.com/questions/46042623/...
user177196

2
このコードは、非常に大きなcsvファイル(GBのオーダー)に最適化されていません
Nisba

91

ディスク上のファイルへのsqlite接続の作成は、読者のための演習として残されています...しかし、今ではpandasライブラリによって可能になった2ライナーがあります

df = pandas.read_csv(csvfile)
df.to_sql(table_name, conn, if_exists='append', index=False)

ありがとうございました。パンダで問題が発生しました。私のcsvは「;」で区切られています エントリに「、」が含まれています。パンダはread_csvでエラーを出します。一時的に置き換えずにカンマでエントリを読み取る設定はありますか?
Alexei Martianov 2016年

3
sep = ';'を使用します。パンダのドキュメントには、これに対処する方法が明確に記載されています。
テネシーレーウェンブルク

3
パンダを使用する方法はありますが、RAMを使用せずに、巨大な.csv(7gb)を使用しています。データフレームとしてインポートしてDBに追加することはできません。
Pablo

1
はい、一度にすべてではなくチャンクで読み取るパンダのメソッドがあります。頭の上から正確に思い出せないのではないでしょうか。chunksize = <number_of_rows>を追加すると、イテレータが返され、それを使用してデータベースにピース単位で追加できます。見つけにくい場合はお知らせください。レシピを探します。
Tennessee Leeuwenburg 2016

1
とても素敵です、@ TennesseeLeeuwenburg。私は必要がなかったので、dfあなたの例を次のように短縮しました:pandas.read_csv(csvfile).to_sql(table_name, conn, if_exists='append', index=False)
keithpjolley

13

私の2セント(より一般的):

import csv, sqlite3
import logging

def _get_col_datatypes(fin):
    dr = csv.DictReader(fin) # comma is default delimiter
    fieldTypes = {}
    for entry in dr:
        feildslLeft = [f for f in dr.fieldnames if f not in fieldTypes.keys()]
        if not feildslLeft: break # We're done
        for field in feildslLeft:
            data = entry[field]

            # Need data to decide
            if len(data) == 0:
                continue

            if data.isdigit():
                fieldTypes[field] = "INTEGER"
            else:
                fieldTypes[field] = "TEXT"
        # TODO: Currently there's no support for DATE in sqllite

    if len(feildslLeft) > 0:
        raise Exception("Failed to find all the columns data types - Maybe some are empty?")

    return fieldTypes


def escapingGenerator(f):
    for line in f:
        yield line.encode("ascii", "xmlcharrefreplace").decode("ascii")


def csvToDb(csvFile, outputToFile = False):
    # TODO: implement output to file

    with open(csvFile,mode='r', encoding="ISO-8859-1") as fin:
        dt = _get_col_datatypes(fin)

        fin.seek(0)

        reader = csv.DictReader(fin)

        # Keep the order of the columns name just as in the CSV
        fields = reader.fieldnames
        cols = []

        # Set field and type
        for f in fields:
            cols.append("%s %s" % (f, dt[f]))

        # Generate create table statement:
        stmt = "CREATE TABLE ads (%s)" % ",".join(cols)

        con = sqlite3.connect(":memory:")
        cur = con.cursor()
        cur.execute(stmt)

        fin.seek(0)


        reader = csv.reader(escapingGenerator(fin))

        # Generate insert statement:
        stmt = "INSERT INTO ads VALUES(%s);" % ','.join('?' * len(cols))

        cur.executemany(stmt, reader)
        con.commit()

    return con

1
if len(feildslLeft)> 0:常にtrueなので、例外が発生します。これを確認して修正してください。
amu61

ストリームで使用できるように、fseek()を使用せずにこれを行う方法はありますか?
mwag 2016年

1
@mwagでは、列タイプのチェックをスキップして、代わりにすべての列をテキストとしてインポートできます。
user5359531

12

.importコマンドは、sqlite3のコマンドラインツールの機能です。Pythonでこれを行うには、csvモジュールなどのPythonの機能を使用してデータをロードし、通常どおりにデータを挿入するだけです。

このように、sqlite3のドキュメントに記載されていないように見える動作に依存するのではなく、挿入する型を制御することもできます。


1
インサートを準備する必要はありません。SQLステートメントのソースとコンパイルされた結果は、キャッシュに保持されます。
John Machin、

@John Machin:SQLiteがこれを行う方法へのリンクはありますか?
Marcelo Cantos

@Marcelo:方法に興味がある場合は(なぜですか)、sqliteソースを確認するか、sqliteメーリングリストで質問してください。
John Machin

@John Machin:興味があるのは、これまでに出会ったすべてのSQLiteドキュメントに、準備されていないステートメントの自動キャッシュについて一言もないからです。私は、SQLステートメントを準備する必要があるかどうかなど、基本的なことを発見するためにソースコードを読んだり、メーリングリストを調べたりする必要があるのは合理的ではないと思います。これに関するあなたの情報源は何ですか?
Marcelo Cantos

4
@Marcelo:実際には、Python sqlite3ラッパーモジュールで行われます。docs.python.org/library/…は「」と言います。sqlite3モジュールは内部でステートメントキャッシュを使用して、SQL解析のオーバーヘッドを回避します。接続用にキャッシュされるステートメントの数を明示的に設定したい場合は、cached_statementsパラメータを設定できます現在実装されているデフォルトでは、100個のステートメントをキャッシュします。 "" "
John Machin

9
#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys, csv, sqlite3

def main():
    con = sqlite3.connect(sys.argv[1]) # database file input
    cur = con.cursor()
    cur.executescript("""
        DROP TABLE IF EXISTS t;
        CREATE TABLE t (COL1 TEXT, COL2 TEXT);
        """) # checks to see if table exists and makes a fresh table.

    with open(sys.argv[2], "rb") as f: # CSV file input
        reader = csv.reader(f, delimiter=',') # no header information with delimiter
        for row in reader:
            to_db = [unicode(row[0], "utf8"), unicode(row[1], "utf8")] # Appends data from CSV file representing and handling of text
            cur.execute("INSERT INTO neto (COL1, COL2) VALUES(?, ?);", to_db)
            con.commit()
    con.close() # closes connection to database

if __name__=='__main__':
    main()

9

バーニーの答えに感謝します!少し微調整する必要がありました-これが私のために働いたものです:

import csv, sqlite3
conn = sqlite3.connect("pcfc.sl3")
curs = conn.cursor()
curs.execute("CREATE TABLE PCFC (id INTEGER PRIMARY KEY, type INTEGER, term TEXT, definition TEXT);")
reader = csv.reader(open('PC.txt', 'r'), delimiter='|')
for row in reader:
    to_db = [unicode(row[0], "utf8"), unicode(row[1], "utf8"), unicode(row[2], "utf8")]
    curs.execute("INSERT INTO PCFC (type, term, definition) VALUES (?, ?, ?);", to_db)
conn.commit()

私のテキストファイル(PC.txt)は次のようになります。

1 | Term 1 | Definition 1
2 | Term 2 | Definition 2
3 | Term 3 | Definition 3

6

あなたはね、右.importに行くための方法ですが、それはSQLite3.exeシェルからコマンドです。この質問に対する上位の回答の多くはネイティブPythonループに関係していますが、ファイルが大きい場合(私のレコードは10 ^ 6から10 ^ 7レコードです)、すべてをパンダに読み込んだり、ネイティブPythonリスト内包/ループを使用したりしないでください。 (ただし、比較のために時間を計っていませんでした)。

大きなファイルの場合、最良のオプションは、事前にを使用して空のテーブルを作成sqlite3.execute("CREATE TABLE...")し、CSVファイルからヘッダーを取り除いてから、を使用subprocess.run()してsqliteのインポート文を実行することです。最後の部分が最も適切だと思うので、それから始めます。

subprocess.run()

from pathlib import Path
db_name = Path('my.db').resolve()
csv_file = Path('file.csv').resolve()
result = subprocess.run(['sqlite3',
                         str(db_name),
                         '-cmd',
                         '.mode csv',
                         '.import '+str(csv_file).replace('\\','\\\\')
                                 +' <table_name>'],
                        capture_output=True)

説明
コマンドラインから、探しているコマンドはですsqlite3 my.db -cmd ".mode csv" ".import file.csv table"subprocess.run()コマンドラインプロセスを実行します。への引数subprocess.run()は、コマンドの後にすべての引数が続くものとして解釈される一連の文字列です。

  • sqlite3 my.db データベースを開きます
  • -cmdデータベースの後にフラグを付けると、sqliteプログラムに複数のfollowコマンドを渡すことができます。シェルでは、各コマンドは引用符で囲む必要がありますが、ここでは、シーケンスの独自の要素である必要があります
  • '.mode csv' あなたが期待することをします
  • '.import '+str(csv_file).replace('\\','\\\\')+' <table_name>'インポートコマンドです。
    残念ながら、サブプロセスはすべての後続-cmdを引用符付きの文字列として渡すため、Windowsディレクトリパスがある場合はバックスラッシュを2倍にする必要があります。

ヘッダーの除去

質問の主なポイントではありませんが、ここで私が使用したものです。繰り返しますが、ファイル全体をメモリに読み込む必要はありませんでした。

with open(csv, "r") as source:
    source.readline()
    with open(str(csv)+"_nohead", "w") as target:
        shutil.copyfileobj(source, target)


4

Guy Lソリューション(Love it)に基づいていますが、エスケープされたフィールドを処理できます。

import csv, sqlite3

def _get_col_datatypes(fin):
    dr = csv.DictReader(fin) # comma is default delimiter
    fieldTypes = {}
    for entry in dr:
        feildslLeft = [f for f in dr.fieldnames if f not in fieldTypes.keys()]        
        if not feildslLeft: break # We're done
        for field in feildslLeft:
            data = entry[field]

            # Need data to decide
            if len(data) == 0:
                continue

            if data.isdigit():
                fieldTypes[field] = "INTEGER"
            else:
                fieldTypes[field] = "TEXT"
        # TODO: Currently there's no support for DATE in sqllite

    if len(feildslLeft) > 0:
        raise Exception("Failed to find all the columns data types - Maybe some are empty?")

    return fieldTypes


def escapingGenerator(f):
    for line in f:
        yield line.encode("ascii", "xmlcharrefreplace").decode("ascii")


def csvToDb(csvFile,dbFile,tablename, outputToFile = False):

    # TODO: implement output to file

    with open(csvFile,mode='r', encoding="ISO-8859-1") as fin:
        dt = _get_col_datatypes(fin)

        fin.seek(0)

        reader = csv.DictReader(fin)

        # Keep the order of the columns name just as in the CSV
        fields = reader.fieldnames
        cols = []

        # Set field and type
        for f in fields:
            cols.append("\"%s\" %s" % (f, dt[f]))

        # Generate create table statement:
        stmt = "create table if not exists \"" + tablename + "\" (%s)" % ",".join(cols)
        print(stmt)
        con = sqlite3.connect(dbFile)
        cur = con.cursor()
        cur.execute(stmt)

        fin.seek(0)


        reader = csv.reader(escapingGenerator(fin))

        # Generate insert statement:
        stmt = "INSERT INTO \"" + tablename + "\" VALUES(%s);" % ','.join('?' * len(cols))

        cur.executemany(stmt, reader)
        con.commit()
        con.close()

4

あなたは、この使用して行うことができますblazeodo効率的に

import blaze as bz
csv_path = 'data.csv'
bz.odo(csv_path, 'sqlite:///data.db::data')

Odoはcsvファイルをdata.dbスキーマの下の(sqliteデータベース)に保存しますdata

またはodo、なしで直接使用しますblaze。どちらの方法でもかまいません。このドキュメントを読む


2
bzは定義されていません:P
holms

そして、彼の内部エラーのため、おそらく非常に古いパッケージです:AttributeError: 'SubDiGraph' object has no attribute 'edge'
holms

また、同じ属性エラーが発生します
。GitHub

2

CSVファイルをPythonプログラムの一部としてインポートする必要がある場合は、簡単かつ効率的にするためos.systemに、以下の提案に沿って使用できます。

import os

cmd = """sqlite3 database.db <<< ".import input.csv mytable" """

rc = os.system(cmd)

print(rc)

重要なのは、データベースのファイル名を指定することにより、データの読み取りにエラーがないと想定して、データが自動的に保存されることです。


1
import csv, sqlite3

def _get_col_datatypes(fin):
    dr = csv.DictReader(fin) # comma is default delimiter
    fieldTypes = {}
    for entry in dr:
        feildslLeft = [f for f in dr.fieldnames if f not in fieldTypes.keys()]        
        if not feildslLeft: break # We're done
        for field in feildslLeft:
            data = entry[field]

        # Need data to decide
        if len(data) == 0:
            continue

        if data.isdigit():
            fieldTypes[field] = "INTEGER"
        else:
            fieldTypes[field] = "TEXT"
    # TODO: Currently there's no support for DATE in sqllite

if len(feildslLeft) > 0:
    raise Exception("Failed to find all the columns data types - Maybe some are empty?")

return fieldTypes


def escapingGenerator(f):
    for line in f:
        yield line.encode("ascii", "xmlcharrefreplace").decode("ascii")


def csvToDb(csvFile,dbFile,tablename, outputToFile = False):

    # TODO: implement output to file

    with open(csvFile,mode='r', encoding="ISO-8859-1") as fin:
        dt = _get_col_datatypes(fin)

        fin.seek(0)

        reader = csv.DictReader(fin)

        # Keep the order of the columns name just as in the CSV
        fields = reader.fieldnames
        cols = []

        # Set field and type
        for f in fields:
            cols.append("\"%s\" %s" % (f, dt[f]))

        # Generate create table statement:
        stmt = "create table if not exists \"" + tablename + "\" (%s)" % ",".join(cols)
        print(stmt)
        con = sqlite3.connect(dbFile)
        cur = con.cursor()
        cur.execute(stmt)

        fin.seek(0)


        reader = csv.reader(escapingGenerator(fin))

        # Generate insert statement:
        stmt = "INSERT INTO \"" + tablename + "\" VALUES(%s);" % ','.join('?' * len(cols))

        cur.executemany(stmt, reader)
        con.commit()
        con.close()

2
コードを適切にフォーマットし、説明を追加してください
実行可能

1

簡単にするために、プロジェクトのMakefileからsqlite3コマンドラインツールを使用できます。

%.sql3: %.csv
    rm -f $@
    sqlite3 $@ -echo -cmd ".mode csv" ".import $< $*"
%.dump: %.sql3
    sqlite3 $< "select * from $*"

make test.sql3次に、既存のtest.csvファイルから、単一のテーブル「test」を含むsqliteデータベースを作成します。その後make test.dump、内容を確認できます。


1

メモリ不足にならないように、csvからデータベースへのデータ転送を分割して分割する必要がある場合があることを発見しました。これは次のように行うことができます:

import csv
import sqlite3
from operator import itemgetter

# Establish connection
conn = sqlite3.connect("mydb.db")

# Create the table 
conn.execute(
    """
    CREATE TABLE persons(
        person_id INTEGER,
        last_name TEXT, 
        first_name TEXT, 
        address TEXT
    )
    """
)

# These are the columns from the csv that we want
cols = ["person_id", "last_name", "first_name", "address"]

# If the csv file is huge, we instead add the data in chunks
chunksize = 10000

# Parse csv file and populate db in chunks
with conn, open("persons.csv") as f:
    reader = csv.DictReader(f)

    chunk = []
    for i, row in reader: 

        if i % chunksize == 0 and i > 0:
            conn.executemany(
                """
                INSERT INTO persons
                    VALUES(?, ?, ?, ?)
                """, chunk
            )
            chunk = []

        items = itemgetter(*cols)(row)
        chunk.append(items)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.