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

이 사용 할 수있는 blaze및 odo효율적를

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.