SQLAlchemy中contains_eager和joinedload的区别

7
在SQLAlchemy中,contains_eagerjoinedload的区别是什么?它们都可用于加载一对多或多对一相关的行。我阅读了关于contains_eager手册,以及有关joinedload手册
它们生成相同的 SQL 语句。
query = session.query(User).\
    outerjoin(adalias, User.addresses).\
    options(contains_eager(User.addresses, alias=adalias)).all()

...

SELECT
    users.user_id AS users_user_id,
    users.user_name AS users_user_name,
    adalias.address_id AS adalias_address_id,
    adalias.user_id AS adalias_user_id,
    adalias.email_address AS adalias_email_address,
    (...other columns...)
FROM users
LEFT OUTER JOIN email_addresses AS email_addresses_1
ON users.user_id = email_addresses_1.user_id


>>> jack = session.query(User).\
... options(joinedload(User.addresses)).\
... filter_by(name='jack').all()

SELECT
    addresses_1.id AS addresses_1_id,
    addresses_1.email_address AS addresses_1_email_address,
    addresses_1.user_id AS addresses_1_user_id,
    users.id AS users_id, users.name AS users_name,
    users.fullname AS users_fullname,
    users.password AS users_password
FROM users
LEFT OUTER JOIN addresses AS addresses_1
    ON users.id = addresses_1.user_id
WHERE users.name = ?
['jack']

有没有人能展示更具体的代码示例?

请参考此答案的实际示例,并将 .options(contains_eager(Child.toys)) 修改为 .options(joinedload(Child.toys)),然后查看 Child.toys 不再作为过滤子集填充。 - metatoaster
1个回答

10
不要忘记多对多和一对一。区别在于使用contains_eager()指示SQLA使用现有的连接或连接来填充关系。这样,您也可以使用筛选子集填充
另一方面,joinedload()会非常努力地使用所需的连接来填充,完全透明,并且不应影响原始查询的结果,正如Joined Eager Loading之禅中所解释的那样。换句话说,您不能使用连接关系进行过滤等操作。

10
值得一提的是:在1-N或N-N关系中,您不应使用contains_eager()joinedload。这是因为SQL结果会在一个或两个方向上产生数据乘法。对于1-N或N-N关系,最好使用selectinloadsubqueryload - szapio
1
通常情况下,选择两者中的一个是正确的,但它们不允许(据我所知)过滤在SQLA中填充关系的相关实体。因此,在需要这样做的情况下,这是一种“选择你的毒药”的情况。希望过滤集合足够小,以至于1端的重复并不重要。 - Ilja Everilä
3
同意,对于小量数据通常不会有影响,但是我亲眼见过有人执行了多个联合加载操作,结果数据库返回的输出比整个数据库还要大25倍 :) - szapio
这在它自己的方式上是很棒的。在之前的工作中,我们曾经因为意外的交叉连接而玩得很开心... - Ilja Everilä
为了完整性,此主题比较了其他加载器,并且答案深入探讨了使用“joinedload”的问题。 - metatoaster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接