【问题标题】:Performance one-to-many relationship in SQLAlchemySQLAlchemy中的性能一对多关系
【发布时间】:2017-04-30 06:40:08
【问题描述】:

我正在尝试与 SqlAlchemy 定义一对多关系,其中我有 Parent 有很多 Child

class Parent(Base):
    __tablename__ = "parent"

    id = Column(String, primary_key = True)
    children = relationship("Child")


class Child(Base):
    __tablename__ = "child"

    id = Column(Integer, primary_key = True) 
    feed_type_id = Column(String, ForeignKey("parent.id"))

从业务规则来看,Parent 没有太多 Child(介于 10 到 30 之间),并且大多数时候我需要访问所有这些,所以我认为 relationship() 检索内存中的所有孩子是个好主意,以便提高表现(第一个问题:我是对的吗?)但我需要得到一个特定的孩子,但我不会这样做:

def search_bar_attr(some_value)
    for bar in foo.bars:
        if(bar.attr == some_value)
            return bar

lazy="dynamic" 返回一个允许查询的列表,但我认为它对“急切”加载很慢,因为动态关系总是查询数据库。

第二个问题:有没有可以满足我所有需求的配置?

【问题讨论】:

  • 这通常很难回答。但是,您可以在查询时specify loading strategy,因此请选择最适合运行查询的上下文的选项。当性能很重要时,我发现最好使用手动构建的 sql 和逻辑来测试查询和函数的性能,然后向后工作并编写相应的 sqlalchemy 表达式。

标签: python sqlalchemy


【解决方案1】:

您可以使用.with_parent 构造与lazy="dynamic" 相同的查询。

class Parent(Base):
    ...
    @property
    def children_dynamic(self):
        return object_session(self).query(Child).with_parent(self, Parent.children)

如果你必须写很多这些,你甚至可以添加一个函数来减少样板:

def dynamicize(rel):
    @property
    def _getter(self):
        return object_session(self).query(rel.parent).with_parent(self, rel)
    return _getter

class Parent(Base):
    ...
    children = relationship("Child")
    children_dynamic = dynamicize(children)

【讨论】:

  • 根据the documentproperty参数可以省略,所以我认为可以缩短为object_session(self).query(Child).with_parent(self)
  • 另外,我无法理解这一点:relationship("Child")object_session(self).query(Child).with_parent(self, Parent.children).all()object_session(self).query(Child).filter(parent=self).all()(假设我们在关系中有 backref='parent')是否等效?
  • @SunQingyao 如果你对children 有一个称为parent 的反向引用,那么.filter(Child.parent == self) 在功能上将等同于.with_parent(self, Parent.children),但我认为SQL 的方式存在细微差别呈现。前者也不太通用,因为它假定一对多的关系。
【解决方案2】:

您不需要使用类似的函数,甚至不需要将所有子对象加载到内存中。
当您想搜索具有某个属性的孩子时,您可以这样做:

# get a session object, usually with sessionmaker() configured to bind to your engine instance
c = session.query(Child).filter_by(some_attribute="some value here").all() # returns a list of all child objects that match the filter
# or: to get a child who belongs to a certain parrent with a certain attribute:
# get the parent object (p)
c = session.query(Child).filter_by(feed_type_id=p.id).filter_by(some_attr="some attribute that belongs to children of the p parrent object")

【讨论】:

  • 两种情况下,您查询具有 10000+ 行的子表我想从父表中过滤,因为只是查询 10 到 30 条记录
【解决方案3】:

没有一种策略可以为您提供一切。但是,您可以选择默认策略,然后覆盖它。 我的建议是:

  • 将lazy = "joined" 添加到您的关系中,这样默认情况下您将获得所有父母。

  • 如果您要查询一组依赖于父对象属性但不需要父对象的子对象,请在查询中使用联接函数并同时引用父对象和子对象

  • 如果您需要构建类似于 lazy = "dynamic" 的查询,请使用 sqlalchemy.orm.defer 运算符关闭您的 lazy = "joined" 急切加载和 loading interface ( 覆盖急切加载,然后使用 with_parent 构造查询。像您使用lazy = "dynamic" 得到的查询

【讨论】:

  • “动态过滤孩子”是什么意思?
  • @Overflow012 澄清了我在答案中的意思;我的意思是你得到了lazy =“dynamic”
猜你喜欢
  • 2019-12-06
  • 1970-01-01
  • 1970-01-01
  • 2016-06-15
  • 2021-08-31
  • 2020-11-01
  • 1970-01-01
  • 2016-12-03
  • 2021-09-07
相关资源
最近更新 更多