【问题标题】:Fastest way to write database table to file in python在python中将数据库表写入文件的最快方法
【发布时间】:2014-01-21 22:12:45
【问题描述】:

我正在尝试从数据库中提取大量数据并将其写入 csv 文件。我试图找出最快的方法是什么。我发现在 fetchall 的结果上运行 writerows 比下面的代码慢 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 具有相当的性能,并且更容易看,所以我想我会使用它。非常感谢!

【问题讨论】:

  • 哪个数据库?哪个接口库?你能给我们一个表模式和一些数据吗?
  • 例如:对于某些数据库,fetchone 从服务器一次获取一行(或者,更好的是,一次获取一个充满行的缓冲区),这使其效率更高而不是fetchall,它显然会获取所有这些,这意味着在它们都准备好之前你不能做任何工作,而且你也可能会浪费很多工作来为它们分配和管理内存。但在其他情况下,fetchone 在幕后(实际上)是fetchall,所以没有优势,实际上是一个小幅放缓。
  • 此外,如果任何数据中有任何制表符、换行符或其他特殊字符,您的代码将生成损坏的 CSV 文件。这可以接受吗?
  • 了解您正在使用哪个数据库系统来回答这个问题至关重要。一般来说,从数据库端创建表可能会更好,这样您就不必担心将其引入 Python 的开销。我猜最快会像stackoverflow.com/questions/1517635/…那样使用COPY

标签: python database performance


【解决方案1】:

鉴于您给我们的帮助很少,很难说得更具体,但是……

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

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 慢得多或快得多,具体取决于您的数据。

但要获得真正有用​​的解释,您必须准确告诉我们您正在使用哪个数据库和库(以及哪个 Python 版本——CPython 3.3.2 的csv 模块似乎比 CPython 2.7 快很多。 5 和 PyPy 2.1/2.7.2 似乎也比 CPython 2.7.5 更快,但是其中任何一个也可能更快地运行您的代码......)等等。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-07
    • 1970-01-01
    • 1970-01-01
    • 2010-11-06
    • 1970-01-01
    • 2020-05-01
    • 2016-02-24
    相关资源
    最近更新 更多