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

Posted

技术标签:

【中文标题】如何在 SQLAlchemy 中使用空集合进行分页查询?【英文标题】:How to do a paged query with empty collections in SQLAlchemy? 【发布时间】:2012-11-28 05:00:37 【问题描述】:

我正在尝试找到一种内存有效的方法来执行分页查询以测试空集合,但似乎无法弄清楚如何在大型数据库上有效地执行它。表格布局使用具有双向反向引用的关联对象。它与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】:

我发现这里缺少一些东西。空块的查询似乎大部分都可以。我看到的内存使用高峰来自代码中几行之前的查询,当时实际成员本身已被删除。

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

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

我补充说:

passive_deletes=True

到Member类的关联关系和:

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 似乎有很大帮助。页面查询在这种情况下似乎也能很好地工作,只是我必须取出偏移量,因为块正在从会话内联中删除。

【讨论】:

以上是关于如何在 SQLAlchemy 中使用空集合进行分页查询?的主要内容,如果未能解决你的问题,请参考以下文章

如何根据集合中的文档位置进行分页? (偏移分页)

Python Flask SQLAlchemy 分页

如何在 Laravel 5 中对合并的集合进行分页?

使用 LINQ 对集合进行分页

sqlalchemy和flask-sqlalchemy的几种分页方法

Python中使用flask_sqlalchemy实现分页效果方法详解