【发布时间】:2021-09-13 02:59:24
【问题描述】:
我的项目中的 Image 和 Tag 表之间存在多对多关系:
tags2images = db.Table("tags2images",
db.Column("tag_id", db.Integer, db.ForeignKey("tags.id", ondelete="CASCADE", onupdate="CASCADE"), primary_key=True),
db.Column("image_id", db.Integer, db.ForeignKey("images.id", ondelete="CASCADE", onupdate="CASCADE"), primary_key=True)
)
class Image(db.Model):
__tablename__ = "images"
id = db.Column(db.Integer, primary_key=True, autoincrement=False)
title = db.Column(db.String(1000), nullable=True)
tags = db.relationship("Tag", secondary=tags2images, back_populates="images", passive_deletes=True)
class Tag(db.Model):
__tablename__ = "tags"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(250), nullable=False, unique=True)
images = db.relationship(
"Image",
secondary=tags2images,
back_populates="tags",
passive_deletes=True
)
我想获取一个标签列表,按它们在图像中的使用次数排序。我的图像和标签表分别包含 ~200.000 和 ~1.000.000 行,因此数据量相当可观。
经过一番折腾,我来到了这个怪物:
db.session.query(Tag, func.count(tags_assoc.c.tag_id).label("total"))\
.join(tags_assoc)\
.group_by(Tag)\
.order_by(text("total DESC"))\
.limit(20).all()
虽然它确实按照我想要的方式返回了一个 (Tag, count) 元组列表,但它需要几秒钟,这不是最佳的。
我发现这篇很有帮助的帖子 (Counting relationships in SQLAlchemy) 帮助我将上述内容简化为
db.session.query(Tag.name, func.count(Tag.id))\
.join(Tag.works)\
.group_by(Tag.id)\
.limit(20).all()
虽然与我的第一次尝试相比,这非常快,但输出显然不再排序。如何让 SQLAlchemy 在保持快速查询的同时产生所需的结果?
【问题讨论】:
-
由于您的
Tag类只有一个有意义的属性Tag.name,并且它被定义为unique=True,那么您可以只使用它作为主键并省略id代理项(自动增量)键。这样,您对关联表的聚合查询将直接返回Tag.namePK,从而可能避免对代理键进行不必要的连接。
标签: python sqlalchemy