如何在SQLAlchemy中使用空集合进行分页查询?

时间:2012-11-28 05:00:37

标签: python sqlalchemy

我正在尝试找到一种内存有效的方法来执行分页查询以测试空集合,但似乎无法弄清楚如何在大型数据库上有效地进行此操作。表格布局使用具有双向backref的关联对象。它与documentation非常相似。

class Association(Base):
    __tablename__ = 'Association'
    assoc_id = Column(Integer, primary_key=True, nullable=False, unique=True)
    member_id = Column(Integer, ForeignKey('Member.id'))
    chunk_id = Column(Integer, ForeignKey('Chunk.id'))
    extra = Column(Text)
    chunk = relationship("Chunk", backref=backref("assoc", lazy="dynamic"))

class Member(Base):
    __tablename__ = 'Member'
    id = Column(Integer, primary_key=True, nullable=False, unique=True)
    assocs = relationship("Association", backref="member", cascade="all, delete", lazy="dynamic")

class Chunk(Base):
    __tablename__ = 'Chunk'
    id = Column(Integer, primary_key=True, nullable=False, unique=True)
    name = Column(Text, unique=True)

如果删除该成员,它将级联并删除该成员的关联。但是,块对象将在数据库中孤立。要删除孤立的块,我可以使用如下查询来测试空集合:

session.query(Chunk).filter(~Chunk.assoc.any())

然后使用以下命令删除块

query.delete(synchronize_session=False)

但是,如果关联和块表很大,那么查询或子查询似乎会加载所有内容并且内存会大量增加。

我已经看到了使用分页查询来限制标准查询的内存使用量的概念here

def page_query(q, count=1000):
    offset = 0
    while True:
        r = False
        for elem in q.limit(count).offset(offset):
            r = True
            yield elem
        offset += count
        if not r:
            break

for chunk in page_query(Session.query(Chunk)):
    print chunk.name

然而,由于内存使用量仍然很高,因此这似乎不适用于空集合查询。有没有办法对像这样的空集合进行分页查询?

1 个答案:

答案 0 :(得分:1)

我发现这里遗漏了一些东西。对空块的查询似乎基本上没问题。我看到的内存使用率峰值是在实际成员本身被删除时,在代码前面的几行查询。

member = session.query(Member).filter(Member.name == membername).one()
session.delete(member)

根据文档,会话(默认情况下)只能删除加载到会话/内存中的对象。当成员被删除时,它将加载它的所有关联,以便按照级联规则删除它们。需要做的是使用passive deletes来绕过关联加载。

我补充说:

passive_deletes=True

到成员类的关联关系,并且:

ondelete='CASCADE'

到Association类的member_id外键。我正在使用SQLite3,并根据docs添加了引擎连接事件的外键支持。

关于孤立块,而不是使用query.delete方法批量删除块。我使用了一个不包含偏移量的页面查询,并在循环中删除了会话中的块,如下所示。到目前为止,我似乎没有任何内存峰值:

def page_query(q):
    while True:
        r = False
        for elem in q.limit(1000):
            r = True
            yield elem
        if not r:
            break

for chunk in page_query(query):
    # Do something with the chunk if needed
    session.delete(chunk)
session.commit()

总而言之,删除具有大集合的父对象时,使用passive_deletes = True似乎有很大帮助。页面查询在这种情况下似乎也很好用,因为我必须取出偏移量,因为从内联会话中删除了块。