在python中将数据库表写入文件的最快方法

时间:2014-01-21 22:12:45

标签: python database performance

我正在尝试从数据库中提取大量数据并将其写入csv文件。我试图找出最快的方法来做到这一点。我发现在一个fetchall结果上运行writer对比下面的代码要慢40%。

with open(filename, 'a') as f:
    writer = csv.writer(f, delimiter='\t')
    cursor.execute("SELECT * FROM table")
    writer.writerow([i[0] for i in cursor.description])

    count = 0
    builder = []
    row = cursor.fetchone()
    DELIMITERS = ['\t'] * (len(row) - 1) + ['\n']
    while row:
        count += 1
        # Add row with delimiters to builder 
        builder += [str(item) for pair in zip(row, DELIMITERS) for item in pair]
        if count == 1000:
            count = 0
            f.write(''.join(builder))
            builder[:] = []
        row = cursor.fetchone()
    f.write(''.join(builder))

编辑:我正在使用的数据库对于我正在为之工作的小公司来说是独一无二的,所以很遗憾,我无法在这方面提供太多信息。我使用jpype连接数据库,因为唯一的连接方式是通过jdbc驱动程序。我正在运行cPython 2.7.5;我会喜欢使用PyPy,但它不能与Pandas一起使用。

由于我正在提取如此大量的行,所以我对使用fetchall犹豫不决,因为我担心会耗尽内存。 row具有相似的性能并且在眼睛上更容易,所以我想我会使用它。非常感谢!

1 个答案:

答案 0 :(得分:3)

有了你让我们继续下去的小事,很难更具体,但是......

我已将您的代码作为一个函数包装起来,并编写了三个替代版本:

def row():
    with open(filename, 'w') as f:
        writer = csv.writer(f, delimiter='\t')
        cursor = db.execute("SELECT * FROM mytable")
        writer.writerow([i[0] for i in cursor.description])
        for row in cursor:
            writer.writerow(row)

def rows():
    with open(filename, 'w') as f:
        writer = csv.writer(f, delimiter='\t')
        cursor = db.execute("SELECT * FROM mytable")
        writer.writerow([i[0] for i in cursor.description])
        writer.writerows(cursor)

def rowsall():
    with open(filename, 'w') as f:
        writer = csv.writer(f, delimiter='\t')
        cursor = db.execute("SELECT * FROM mytable")
        writer.writerow([i[0] for i in cursor.description])
        writer.writerows(cursor.fetchall())

请注意,最后一个是你说你试过的那个。

现在,我写了这个测试驱动程序:

def randomname():
    return ''.join(random.choice(string.ascii_lowercase) for _ in range(30))

db = sqlite3.connect(':memory:')
db.execute('CREATE TABLE mytable (id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR)')
db.executemany('INSERT INTO mytable (name) VALUES (?)',
               [[randomname()] for _ in range(10000)])

filename = 'db.csv'

for f in manual, row, rows, rowsall:
    t = timeit.timeit(f, number=1)
    print('{:<10} {}'.format(f.__name__, t))

以下是结果:

manual     0.055549702141433954
row        0.03852885402739048
rows       0.03992213006131351
rowsall    0.02850699401460588

因此,您的代码所需的时间几乎是我测试中调用fetchallwriterows的两倍!

然而,当我与其他数据库重复类似的测试时,rowsallmanual快20%到更慢15%(从不慢40%,但多达15%)......但rowrows总是明显快于manual

我认为解释是您的自定义代码明显慢于csv.writerows,但在某些数据库中,使用fetchall代替fetchone(或只是迭代光标)会减慢速度显着下降。对于内存中的sqlite3数据库而言,这是不正确的原因是fetchone正在执行与fetchall相同的所有工作,然后一次为您提供一个列表;使用远程数据库,fetchone可以执行任何操作,从获取所有行,一次获取缓冲区,一次获取一行,使其可能比fetchall更慢或更快,具体取决于csv关于你的数据。

但是,对于一个非常有用的解释,您必须告诉我们您正在使用哪个数据库和库(以及哪个Python版本-CPython 3.3.2&#39; {{1}}模块似乎比CPython 2.7.5快得多,PyPy 2.1 / 2.7.2似乎也比CPython 2.7.5更快,但是其中任何一个也可能更快地运行你的代码......)等等上。