Python CSV到SQLite

cgvd09ve  于 2023-07-31  发布在  Python
关注(0)|答案(6)|浏览(115)

我正在“转换”一个大的(~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表)?

**EDIT --我尝试按照建议直接将csv导入sqlite,但结果发现我的文件在字段中有逗号(例如:"My title, comma")。这会导致导入错误。似乎出现了太多这样的事件,无法手动编辑文件...

还有别的想法吗?**

ux6nzvsh

ux6nzvsh1#

  • 克里斯 * 是正确的使用交易;将数据分成块,然后存储它。

除非已经在事务中,否则每个SQL语句都有一个新的事务开始。这是非常昂贵的,因为它需要为每个语句重新打开、写入和关闭日志文件。这可以通过使用开始 Package SQL语句序列来避免;结束交易;声明。对于不改变数据库的语句也可以获得这种加速。”- Source: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)

字符串

m528fe3b

m528fe3b2#

可以直接导入CSV:

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

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

wgeznvg7

wgeznvg73#

正如Chris和Sam所说,事务确实提高了很多插入性能。
请允许我推荐另一种选择,使用一套Python实用程序来处理CSV,csvkit
要安装:

pip install csvkit

字符串
来解决你的问题

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

68bkxrlz

68bkxrlz4#

尝试使用交易。

begin    
insert 50,000 rows    
commit

字符串
这将定期提交数据,而不是每行提交一次。

wnrlj8wa

wnrlj8wa5#

Pandas可以很容易地将大文件分块加载到数据库中。将CSV文件读入Pandas DataFrame,然后使用Pandas SQL编写器(因此Pandas会完成所有繁重的工作)。下面是如何以100,000行块的形式加载数据。

import pandas as pd

orders = pd.read_csv('path/to/your/file.csv')
orders.to_sql('orders', conn, if_exists='append', index = False, chunksize=100000)

字符串
现代的Pandas版本非常有性能。不要重新发明轮子。请参阅here了解更多信息。

rhfm7lfc

rhfm7lfc6#

cursor.executemanycursor.execute

根据以下基准:https://stackoverflow.com/a/76659706/895245cursor.executemany应该比for循环快得多(观察到3倍),如:https://stackoverflow.com/a/7137270/895245. executemany在单个事务中运行,但在没有任何显式BEGIN/COMMIT的情况下,默认情况下循环也是如此:加速是由于一些其它因素。
main.py

from pathlib import Path
import csv
import sqlite3

f = 'tmp.sqlite'
Path(f).unlink(missing_ok=True)
connection = sqlite3.connect(f)
cursor = connection.cursor()
cursor.execute("CREATE TABLE t (x integer)")
cursor.executemany('INSERT INTO t VALUES (?)', csv.reader(open('10m.csv', 'r')))
connection.commit()
connection.close()

字符串
测试:

python -c 'for i in range(10000000): print(i)' > 10m.csv
time ./main.py


它在9秒内完成,因此与在以下中使用的没有csv读取的虚拟range()循环相比,速度只有很小的减慢:https://stackoverflow.com/a/76659706/895245
要从CSV中选择或预处理列,我们现在可以使用generator expression,例如。沿着以下方针:

python -c 'for i in range(10): print(f"{i},{i*2},{i*4}")' > 10x3.csv
cursor.execute("CREATE TABLE t (x integer, z integer)")
cursor.executemany('INSERT INTO t VALUES (?, ?)',
    ((d[0], d[2]) for d in csv.reader(open('10x3.csv', 'r'))))

csvkit速度慢

我希望csvkit,前面提到过:https://stackoverflow.com/a/9913925/895245可以更好地支持这个用例,但目前它比上面的最小手动executemany脚本慢得多,例如:

echo x > 10m-head.csv
python -c 'for i in range(10000000): print(i)' >> 10m-head.csv
time csvsql --db sqlite:///tmp.sqlite --insert --table t 10m-head.csv


给出1 m25 s。

理论限速sqlite .import

看看我们离Python有多远,假设没有什么可以击败更原生的.import命令:

sqlite3 tmp.sqlite 'create table t(x integer)'
time sqlite3 tmp.sqlite ".import --csv 10m.csv t"


结果在我的SSD:5.8s。比Python快得多,但很高兴看到我们也相差不远。
在Ubuntu 23.04、Python 3.11.2、Lenovo ThinkPad P51、SSD上测试:Samsung MZVLB 512 HAJQ-000 L7 512 GB SSD,3 GB/s标称速度,csvkit==1.0.7,sqlite 3.40.1。

相关问题