减少python中sqlite3 execute / fetchone的时间

时间:2017-09-28 12:41:59

标签: python sqlite

上下文

我正在使用专有格式的多个文件来存储电力系统解决方案的结果。数据格式相当简单,但每个结果文件大约为50MB。提供了一个用于查询文件格式的API,但我需要进行大量查询,并且API非常慢。

我编写了一个程序,使用API​​将这些文件中的几个相互比较,并使其运行几个小时无济于事。我的下一个想法是对文件进行一次传递,将我需要的数据存储到sqlite3数据库中,然后查询。这让我在20分钟内获得了成绩。好多了。重组数据以尽可能避免JOIN:12分钟。将.db文件存储在临时本地位置而不是网络上:8.5分钟。

进一步改进

该程序的当前速度或多或少可以容忍,但该程序在完成后每天会运行很多次。目前,62%的运行时间用于721次.execute / .fetchone调用。

      160787763 function calls (160787745 primitive calls) in 503.061 seconds
Ordered by: internal time
List reduced from 1507 to 20 due to restriction <20>
ncalls  tottime  percall  cumtime  percall filename:lineno(function)
   721  182.869    0.254  182.869    0.254 {method 'fetchone' of 'sqlite3.Cursor' objects}
   721  129.355    0.179  129.355    0.179 {method 'execute' of 'sqlite3.Cursor' objects}
 24822   45.734    0.002   47.600    0.002 {method 'executemany' of 'sqlite3.Connection' objects}

由于在这一小部分花了这么多时间,我想在我前进之前我会要求任何改进它的想法。我觉得我可能会遗漏一些更有经验的眼睛会抓到的东西。该程序的这个特定部分基本上是这样的结构:

for i, db in enumerate(dbs):
    for key, vals in dict.iteritems():
        # If it already has a value, no need to get a comparison value
        if not vals[i]:
            solution_id = key[0]
            num = key[1]

            # Only get a comparison value if the solution is valid for the current db
            if solution_id in db.valid_ids:
                db.cur.execute("""SELECT value FROM table WHERE solution == ? AND num == ?""",
                               (solution_id, num))
                try:
                    vals[i] = db.cur.fetchone()[0]
                # .fetchone() could have returned None, no __getitem__
                except TypeError:
                    pass

dict结构是:

dict = {(solution_id, num): [db1_val, db2_val, db3_val, db4_val]}

每个条目至少有一个db_val,其他条目 None 。上面循环的目的是填充每个可以填充的db_val点,这样就可以比较值。

问题

我已经读过sqlite3 SELECT语句只能用.execute执行,这样就无法使用.executemany(这节省了我在INSERTS上的大量时间)。我也读过python文档,直接从连接对象使用.execute可以更有效,但我不能这样做,因为我需要获取数据。

是否有更好的方法来构建循环或查询,以最大限度地减少在.execute和.fetchone语句上花费的时间?

答案

根据CL和rocksportrocker提供的答案,我改变了我的表创建语句(简化版):

CREATE TABLE table(
solution integer, num integer, ..., value real,
FOREIGN KEY (solution) REFERENCES solution (id),
FOREIGN KEY (num) REFERENCES nums (id)
);

为:

CREATE TABLE table(
solution integer, num integer, ..., value real,
PRIMARY KEY (solution, num),
FOREIGN KEY (solution) REFERENCES solution (id),
FOREIGN KEY (num) REFERENCES nums (id)
) WITHOUT ROWID;

在我的测试用例中,

  • 文件大小保持不变
  • .executemany INSERT语句从~46秒增加到~69秒
  • .execute SELECT语句从~129减少到~5秒
  • .fetchone语句从〜183减少到~0秒
  • 总时间从~503秒减少到~228秒,原始时间的45%

仍然欢迎任何其他改进,希望这可以成为其他SQL新手的一个很好的参考问题。

2 个答案:

答案 0 :(得分:1)

fetchone()if(isset($_POST['submitted']) == 1) 调用是数据库完成所有工作的地方。

要加快查询速度,必须将查阅列编入索引。要节省空间,可以使用聚簇索引,即将表格设为WITHOUT ROWID table

答案 1 :(得分:0)

您是否考虑在solution列上插入索引?会增加.db文件的插入时间和大小。