Python을 사용하여 CSV 파일을 sqlite3 데이터베이스 테이블로 가져 오기


106

CSV 파일이 있고 Python을 사용하여이 파일을 sqlite3 데이터베이스로 대량 가져오고 싶습니다. 명령은 ".import ....."입니다. 하지만 이렇게 작동 할 수없는 것 같습니다. 누구든지 sqlite3에서 수행하는 방법에 대한 예제를 줄 수 있습니까? 만일을 대비하여 창문을 사용하고 있습니다. 감사


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 ()를 호출하여 데이터베이스에 대한 연결을 닫습니다.
Jonas

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는 ';'로 구분됩니다. 항목에 ','가 있습니다. panda는 read_csv에 오류를 제공합니다. 임시로 대체하지 않고 쉼표로 항목을 읽는 설정이 있습니까?
알렉세이 Martianov

3
sep = ';'를 사용하십시오. pandas 문서는이를 처리하는 방법을 명확하게 설명합니다.
테네시 Leeuwenburg

3
pandas를 사용하는 방법이 있지만 RAM을 사용하지 않고 있습니까?, 나는 거대한 .csv (7GB)를 가지고 있으며 데이터 프레임으로 가져올 수 없으며 DB에 추가됩니다.
Pablo

1
예, 판다에는 한 번에 모두 읽는 것이 아니라 청크로 읽는 방법이 있습니다. 머리 꼭대기에서 정확히 기억할 수없는 것이 두렵습니다. chunksize = <number_of_rows>를 추가 한 다음 데이터베이스에 부분적으로 추가하는 데 사용할 수있는 반복기를 다시 가져옵니다. 찾는 데 문제가 있으면 알려주세요. 레시피를 찾아 볼 수 있습니다.
Tennessee Leeuwenburg

1
@TennesseeLeeuwenburg 아주 좋습니다. 나는 그럴 필요가 df없었기 때문에 당신의 예를 다음과 같이 pandas.read_csv(csvfile).to_sql(table_name, conn, if_exists='append', index=False)
줄였습니다

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
len (feildslLeft)> 0 : 항상 true이므로 예외가 발생합니다. 이것을 검토하고 수정하십시오.
amu61

fseek ()없이이를 수행 할 수있는 방법이 있습니까? 그러면 스트림에서 사용할 수 있습니다.
mwag 2011

1
@mwag 열 유형 검사를 건너 뛰고 열을 모두 텍스트로 가져올 수 있습니다.
user5359531

12

.import명령은 sqlite3 명령 줄 도구의 기능입니다. Python에서이를 수행하려면 csv module 과 같은 Python의 기능을 사용하여 데이터를로드하고 평소와 같이 데이터를 삽입해야합니다.

이런 식으로 sqlite3의 문서화되지 않은 동작에 의존하는 대신 삽입되는 유형을 제어 할 수도 있습니다.


1
인서트를 준비 할 필요가 없습니다. SQL 문의 소스 및 컴파일 된 결과는 캐시에 보관됩니다.
John Machin 2010-06-17

@John Machin : SQLite가이를 수행하는 방법에 대한 링크가 있습니까?
Marcelo Cantos 2010-06-17

@Marcelo : 그것이 어떻게되었는지 (왜?)에 관심이 있다면, sqlite 소스를 보거나 sqlite 메일 링리스트에서 물어보세요.
John Machin

@John Machin : 내가 본 모든 SQLite 문서에서 준비되지 않은 명령문의 자동 캐싱에 대한 한 단어가 없기 때문에 관심이 있습니다. SQL 문을 준비해야하는지 여부와 같은 기본적인 것을 발견하기 위해 소스 코드를 읽거나 메일 링리스트를 조사하는 것이 합리적이지 않다고 생각합니다. 이것에 대한 정보의 출처는 무엇입니까?
Marcelo Cantos 2010-06-17

4
@Marcelo : 실제로 Python sqlite3 래퍼 모듈에서 수행됩니다. docs.python.org/library/… 는 "" "라고 말합니다. sqlite3 모듈은 SQL 구문 분석 오버 헤드를 피하기 위해 내부적으로 명령문 캐시를 사용합니다. 연결에 대해 캐시되는 명령문 수를 명시 적으로 설정하려면 cached_statements 매개 변수를 설정할 수 있습니다. . 현재 구현 기본값은 캐시에 100 문이다 "" ".
존 머신

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

bernie의 답변에 감사드립니다 ! 약간 조정해야했습니다. 여기에 저에게 효과적이었습니다.

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

7

당신은 .import갈 길이 맞지만 그것은 SQLite3.exe 쉘의 명령입니다. 이 질문에 대한 많은 상위 답변에는 네이티브 Python 루프가 포함되어 있지만 파일이 큰 경우 (내 파일이 10 ^ 6에서 10 ^ 7 레코드 인 경우) 모든 것을 pandas로 읽거나 네이티브 Python 목록 이해 / 루프를 사용하는 것을 피하고 싶습니다. (비교를 위해 시간을 정하지는 않았지만).

대용량 파일의 경우 가장 좋은 방법은를 사용하여 미리 빈 테이블을 만들고 sqlite3.execute("CREATE TABLE...")CSV 파일에서 헤더를 제거한 다음 subprocess.run()sqlite의 import 문을 실행 하는 데 사용 하는 것입니다. 마지막 부분은 내가 가장 적절하다고 믿기 때문에 시작하겠습니다.

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()명령 줄 프로세스를 실행합니다. to 인수 subprocess.run()는 모든 인수가 뒤 따르는 명령으로 해석되는 문자열 시퀀스입니다.

  • sqlite3 my.db 데이터베이스를 엽니 다
  • -cmd데이터베이스 뒤에 플래그를 지정하면 sqlite 프로그램에 여러 후속 명령을 전달할 수 있습니다. 셸에서 각 명령은 따옴표로 묶어야하지만 여기서는 시퀀스의 고유 한 요소 여야합니다.
  • '.mode csv' 당신이 기대 한대로
  • '.import '+str(csv_file).replace('\\','\\\\')+' <table_name>'가져 오기 명령입니다.
    불행히도 하위 프로세스는 모든 후속 작업 -cmd을 인용 문자열로 전달 하므로 Windows 디렉토리 경로가있는 경우 백 슬래시를 두 배로 늘려야합니다.

헤더 벗기기

질문의 요점은 아니지만 여기에 제가 사용한 것이 있습니다. 다시 말하지만, 나는 어떤 시점에서도 전체 파일을 메모리로 읽고 싶지 않았습니다.

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'개체가 어떤 속성 '엣지'가 없다
HOLMS

또한 동일한 속성 오류가 발생합니다.하지만 GitHub에 대한 의견이있는 것 같습니다.
user791411

2

CSV 파일을 파이썬 프로그램의 일부로 가져와야하는 경우 단순성과 효율성을 위해 다음에서 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"를 사용하여 기존 test.csv 파일에서 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.