【问题标题】:Efficient design of generator for SQLite3 executemany() insert on several tablesSQLite3 executemany() 在多个表上插入的生成器的高效设计
【发布时间】:2019-03-07 16:25:31
【问题描述】:

我正在将一堆大型 xml 文件解析为 python 中的 sqlite3 数据库。据我所知,(尽管我非常愿意并寻求更多性能选项)性能更高的选项是 sqlite3 的 executemany() 插入函数。

我目前正在做的事情的要点如下:

document_dir = '/documents'

Document = named_tuple('Document', 'doc_id doc_title doc_mentioned_people ... etc')
People = named_tuple('People', 'doc_id first_name last_name ... ') 

class DocumentXML(object):
    """
    ... there's some stuff here, but you get the idea

    """

    def parse_document(path):
        """
        This object keeps track of the current 'document' type element from a cElementTree.iterparse() elsewhere

        I've simplified things here, but you can get the idea that this is providing a named tuple for a generator
        """
        doc_id = _current_element.findall(xpath = '../id')[0].text
        doc_title = _current_element.findall(xpath = '../title')[0].text

        # parse lists of people here

        doc_mentioned_people = People(first_name, last_name, ..., person_id)
        #etc...
        return Document(doc_id, doc_title, doc_mentioned_people, ..., etc)

def doc_generator():
    documents = parse_document(document_dir)
    for doc in documents:
        yield doc.id, doc.title, ..., doc.date



# Import into Table 1
with cursor(True) as c:
        c.executemany("INSERT INTO Document VALUES (?,?,?,?,?,?,?,?,?,?,?);", doc_generator())



def people_generator():
    documents = parse_document(document_dir)
    for doc in documents:
        people = doc.people
        yield people.firstname, people.lastname ..., people.eyecolor


# Import into Table 2
with cursor(True) as c:
        c.executemany("INSERT INTO Document VALUES (?,?,?,?,?,?,?,?,?,?,?);", people_generator())


# This goes on for several tables...

如您所见,这里的效率非常低下。每个 xml 文件都被一遍又一遍地解析,解析次数与数据库中的表相同。

我想只使用 XML 的一个解析(因为我可以在一个命名元组中产生所有相关信息),但将结构保持为生成器,以免将内存需求扩大到不可行的水平。

有什么好办法吗?

我的尝试一直围绕着使用带有双插入类型语句的executemany,例如:

c.executemany("
    INSERT INTO Document VALUES (?,?,?,?,?,?,?,?,?,?,?);
    INSERT INTO People VALUES (?,?,?,?,?,?,?); 
    INSERT INTO Companies VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?); 
    INSERT INTO Oils VALUES (?,?,?,?,?,?,?); 
    INSERT INTO Physics VALUES (?,?,?,?,?,?,?,?,?,?,?)",
        complete_data_generator())

complete_data_generator() 产生所有相关的结构化信息;但是,我知道这可能行不通。

有没有更好的方法来构建它以提高性能?

【问题讨论】:

    标签: python database performance sqlite


    【解决方案1】:

    如果您的小文档很少,您会将所有内容都加载到内存中,并且不会再为重新解析文档而烦恼。

    如果您只有一个表来提供数据,那么生成器方法就可以了。

    如果这两种方法都不合适,我会尝试中级方法:

    • 解析一堆XML文件,积累了一些doc元素
    • 当您有 合理 数量的可用文档时,您会暂停解析,并开始在该数量的文档上使用 executemany 提供数据库表
    • 在插入该 文档后,您可以选择释放 SQLite 日志文件,然后继续解析

    优点:

    • 文件只解析一次
    • 可以通过中间提交控制 SQLite 数据库上的负载
    • 你还在用executemany

    缺点:

    • 多次调用executemany,具体取决于数据量
    • 每次提交都需要一些时间

    【讨论】:

      猜你喜欢
      • 2016-01-06
      • 1970-01-01
      • 2015-12-04
      • 2019-01-30
      • 2015-01-21
      • 1970-01-01
      • 1970-01-01
      • 2019-07-10
      • 2012-11-15
      相关资源
      最近更新 更多