Nhập tệp CSV vào bảng cơ sở dữ liệu sqlite3 bằng Python


106

Tôi có một tệp CSV và tôi muốn nhập hàng loạt tệp này vào cơ sở dữ liệu sqlite3 của mình bằng Python. lệnh là ".import .....". nhưng có vẻ như nó không thể hoạt động như thế này. Bất cứ ai có thể cho tôi một ví dụ về cách làm điều đó trong sqlite3? Tôi đang sử dụng cửa sổ chỉ trong trường hợp. Cảm ơn


3
Vui lòng cung cấp lệnh thực sự không hoạt động và thông báo lỗi thực sự . "nhập khẩu ...." có thể là bất kỳ thứ gì. "không thể hoạt động" là quá mơ hồ để chúng tôi đoán tại. Nếu không có chi tiết, chúng tôi không thể giúp đỡ.
S.Lott

2
lệnh thực tế như tôi đã nói là ".import" và nó thông báo lỗi cú pháp mới ".import"
Hossein

10
Hãy thực sự đăng lệnh thực sự trong câu hỏi. Vui lòng thực sự đăng thông báo lỗi thực tế trong câu hỏi. Vui lòng không thêm bình luận chỉ lặp lại những điều. Vui lòng cập nhật câu hỏi bằng bản sao và dán thực tế những gì bạn đang thực sự làm.
S.Lott

Câu trả lời:


133
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
Trong trường hợp bạn gặp vấn đề tương tự, tôi đã làm: Đảm bảo thay đổi col1 và col2 thành tiêu đề cột trong tệp csv. Và đóng kết nối với cơ sở dữ liệu bằng cách gọi con.close () ở cuối.
Jonas

1
Cảm ơn, @Jonas. Bài đã cập nhật.
Mechanical_meat

Tôi tiếp tục nhận được not all arguments converted during string formattingkhi tôi thử phương pháp này.
Whitecat

Tôi đã thử phương pháp này, nhưng nó không hiệu quả với tôi. Bạn có thể kiểm tra tập dữ liệu của tôi ở đây (chúng rất bình thường, ngoại trừ một số cột có giá trị trống) và thử nhập chúng bằng mã của bạn? stackoverflow.com/questions/46042623/…
user177196

2
Mã này không được tối ưu hóa cho các tệp csv rất lớn (đơn hàng GB)
Nisba

91

Tạo kết nối sqlite với tệp trên đĩa được coi là bài tập cho người đọc ... nhưng giờ đây đã có một lớp lót hai lớp được thư viện gấu trúc thực hiện

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

cảm ơn bạn. Tôi gặp sự cố với gấu trúc. csv của tôi được phân tách bằng ';' và có ',' trong các mục nhập. panda đưa ra lỗi trên read_csv. bất kỳ cài đặt nào để đọc các mục nhập có dấu phẩy w / out tạm thời thay thế?
Alexei Martianov

3
sử dụng sep = ';'. Tài liệu về gấu trúc nêu rõ cách giải quyết vấn đề này.
Tennessee Leeuwenburg

3
có cách nào để sử dụng gấu trúc nhưng không sử dụng RAM không ?, tôi có .csv (7gb) rất lớn, tôi không thể nhập dưới dạng khung dữ liệu và sau đó được nối vào DB.
Pablo

1
Vâng, có một phương pháp ở gấu trúc sẽ đọc theo từng phần thay vì tất cả cùng một lúc. Tôi e rằng tôi không thể nhớ lại chính xác trên đỉnh đầu của mình. Tôi nghĩ rằng bạn thêm chunksize = <number_of_rows>, và sau đó bạn nhận lại một trình vòng lặp mà sau đó bạn có thể sử dụng để thêm từng phần vào cơ sở dữ liệu. Hãy cho tôi biết nếu bạn gặp khó khăn khi tìm và tôi có thể tìm ra công thức.
Tennessee Leeuwenburg

1
Rất tuyệt, @TennesseeLeeuwenburg. Tôi không có nhu cầu dfvì vậy tôi rút ngắn ví dụ của bạn xuống:pandas.read_csv(csvfile).to_sql(table_name, conn, if_exists='append', index=False)
keithpjolley

13

2 xu của tôi (chung chung hơn):

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: luôn đúng, vì vậy hãy nâng một ngoại lệ. Hãy xem lại và sửa lỗi này.
amu61

Có cách nào để thực hiện việc này mà không cần phải fseek () để có thể sử dụng nó trên các luồng không?
mwag

1
@mwag, bạn chỉ có thể bỏ qua kiểm tra loại cột và nhập tất cả các cột dưới dạng văn bản.
user5359531

12

Các .importlệnh là một tính năng của công cụ sqlite3 dòng lệnh. Để thực hiện điều đó bằng Python, bạn chỉ cần tải dữ liệu bằng bất kỳ tiện ích nào mà Python có, chẳng hạn như mô-đun csv và chèn dữ liệu như bình thường.

Bằng cách này, bạn cũng có quyền kiểm soát những loại được chèn vào, thay vì dựa vào hành vi dường như không có giấy tờ của sqlite3.


1
Không cần chuẩn bị phụ trang. Nguồn của các câu lệnh SQL và kết quả đã biên dịch được lưu trong bộ nhớ cache.
John Machin

@John Machin: Có liên kết đến cách SQLite thực hiện điều này không?
Marcelo Cantos

@Marcelo: Nếu bạn quan tâm đến CÁCH nó được thực hiện (tại sao?), Hãy tìm trong nguồn sqlite hoặc hỏi trên danh sách gửi thư sqlite.
John Machin

@John Machin: Tôi quan tâm vì trong tất cả các tài liệu SQLite mà tôi đã xem qua, không có một từ nào về bộ nhớ đệm tự động của các câu lệnh chưa chuẩn bị. Tôi không nghĩ là hợp lý khi phải đọc mã nguồn hoặc thăm dò danh sách gửi thư để khám phá một thứ cơ bản như liệu tôi có nên chuẩn bị các câu lệnh SQL của mình hay không. Nguồn thông tin của bạn về điều này là gì?
Marcelo Cantos

4
@Marcelo: Trên thực tế, nó được thực hiện trong mô-đun trình bao bọc Python sqlite3. docs.python.org/library/… said "" "Nội bộ mô-đun sqlite3 sử dụng bộ đệm ẩn câu lệnh để tránh chi phí phân tích cú pháp SQL. Nếu bạn muốn đặt rõ ràng số lượng câu lệnh được lưu trong bộ nhớ cache cho kết nối, bạn có thể đặt tham số cache_statements . Giá trị mặc định hiện hành là bộ nhớ cache 100 báo cáo """.
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

Rất cám ơn câu trả lời của bernie ! Phải điều chỉnh nó một chút - đây là những gì hiệu quả với tôi:

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()

Tệp văn bản của tôi (PC.txt) trông giống như sau:

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

7

Bạn đúng đó .import là cách để đi, nhưng đó là một lệnh từ trình bao SQLite3.exe. Rất nhiều câu trả lời hàng đầu cho câu hỏi này liên quan đến các vòng lặp gốc python, nhưng nếu tệp của bạn lớn (của tôi là 10 ^ 6 đến 10 ^ 7 bản ghi), bạn muốn tránh đọc mọi thứ thành gấu trúc hoặc sử dụng vòng lặp / hiểu danh sách python gốc (mặc dù tôi đã không cho họ thời gian để so sánh).

Đối với các tệp lớn, tôi tin rằng tùy chọn tốt nhất là tạo bảng trống trước bằng cách sử dụng sqlite3.execute("CREATE TABLE..."), tách các tiêu đề khỏi tệp CSV của bạn và sau đó sử dụng subprocess.run()để thực thi câu lệnh nhập của sqlite. Vì phần cuối cùng là phần tôi tin rằng phù hợp nhất nên tôi sẽ bắt đầu với phần đó.

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)

Giải thích
Từ dòng lệnh, lệnh bạn đang tìm là sqlite3 my.db -cmd ".mode csv" ".import file.csv table". subprocess.run()chạy một quy trình dòng lệnh. Đối số tới subprocess.run()là một chuỗi các chuỗi được hiểu là một lệnh, theo sau là tất cả các đối số của nó.

  • sqlite3 my.db mở cơ sở dữ liệu
  • -cmdcờ sau khi cơ sở dữ liệu cho phép bạn chuyển nhiều lệnh theo dõi vào chương trình sqlite. Trong shell, mỗi lệnh phải nằm trong dấu ngoặc kép, nhưng ở đây, chúng chỉ cần là phần tử riêng của chuỗi
  • '.mode csv' làm những gì bạn mong đợi
  • '.import '+str(csv_file).replace('\\','\\\\')+' <table_name>'là lệnh nhập.
    Thật không may, vì quy trình con chuyển tất cả các phần theo dõi thành các -cmdchuỗi được trích dẫn, bạn cần tăng gấp đôi dấu gạch chéo ngược của mình nếu bạn có đường dẫn thư mục windows.

Tước tiêu đề

Không thực sự là điểm chính của câu hỏi, nhưng đây là những gì tôi đã sử dụng. Một lần nữa, tôi không muốn đọc toàn bộ tệp vào bộ nhớ tại bất kỳ thời điểm nào:

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

4

Dựa trên giải pháp Guy L (Love it) nhưng có thể xử lý các trường đã thoát.

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

Bạn có thể làm điều này bằng cách sử dụng blaze& odohiệu quả

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

Odo sẽ lưu trữ tệp csv vào data.db(cơ sở dữ liệu sqlite) trong lược đồdata

Hoặc bạn sử dụng odotrực tiếp, không cần blaze. Cách nào cũng được. Đọc tài liệu này


2
bz không được định nghĩa: P
Holms

và nó có thể là gói rất cũ vì lỗi bên trong của anh ấy: AttributeError: Đối tượng 'SubDiGraph' không có thuộc tính 'edge'
holms

Cũng nhận được lỗi tương tự thuộc tính: dường như có những ý kiến trên GitHub cho nó, mặc dù
user791411

2

Nếu tệp CSV phải được nhập như một phần của chương trình python, thì để đơn giản và hiệu quả, bạn có thể sử dụng os.systemtheo các dòng được đề xuất như sau:

import os

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

rc = os.system(cmd)

print(rc)

Vấn đề là bằng cách chỉ định tên tệp của cơ sở dữ liệu, dữ liệu sẽ tự động được lưu, giả sử không có lỗi khi đọc nó.


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
Vui lòng định dạng mã của bạn đúng cách và thêm một số giải thích
thực thi được

1

vì sự đơn giản, bạn có thể sử dụng công cụ dòng lệnh sqlite3 từ Makefile của dự án.

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

make test.sql3sau đó tạo cơ sở dữ liệu sqlite từ tệp test.csv hiện có, với một bảng duy nhất "test". sau đó bạn có thể make test.dumpxác minh nội dung.


1

Tôi nhận thấy rằng có thể cần phải chia nhỏ việc chuyển dữ liệu từ csv sang cơ sở dữ liệu thành nhiều phần để không hết bộ nhớ. Điều này có thể được thực hiện như thế này:

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.