On Dec 18, 2012, at 3:50 PM, Gerald Thibault wrote:

> I've attached an example showing the issue. I have a class, Test, with a 
> single polymorphic subclass, TestOne. I also have TestChild, which has a fk 
> to Test, and a relation using that fk.
> 
> When I execute:
> 
> session.query(TestChild) \
>         .options(joinedload(TestChild.parent)) \
>         .get(1)
> 
> I see this query:
> 
> SELECT test_children.id AS test_children_id, test_children.test_id AS 
> test_children_test_id, anon_1.test_id AS anon_1_test_id, anon_1.test_type AS 
> anon_1_test_type, anon_1.test_name AS anon_1_test_name, anon_1.test1_id AS 
> anon_1_test1_id, anon_1.test1_value1 AS anon_1_test1_value1 
> FROM test_children LEFT OUTER JOIN (SELECT test.id AS test_id, test.type AS 
> test_type, test.name AS test_name, test1.id AS test1_id, test1.value1 AS 
> test1_value1 
> FROM test LEFT OUTER JOIN test1 ON test.id = test1.id) AS anon_1 ON 
> anon_1.test_id = test_children.test_id 
> WHERE test_children.id = 1
> 
> What I would like to see is this:
> 
> SELECT <whatever>
> FROM test_children 
> LEFT OUTER JOIN test on test_children.test_id = test.id
> LEFT OUTER JOIN test1 on test1.id = test.id
> WHERE test_children.id = 1
> 
> How can I accomplish this? Also, the join type doesn't matter, they could be 
> inner joins too, the important thing is getting rid of the subquery because 
> it's completely locking up our database.

This is how joining from a left side to a polymorphic or inner-joined subclass 
right side works - the target is always wrapped inside of a subquery, as 
SQLAlchemy was written without ever resorting to the syntax of "x JOIN (y JOIN 
z)", as this syntax was not supported by many backends when SQLA was created.  
SQLite still does not support this syntax.   

Hence when using ORM-generated queries you will always get "x [LEFT OUTER] JOIN 
(select * FROM y [LEFT OUTER] JOIN z) AS anon_1".  

Additionally, the aspect above that is "y [LEFT OUTER] JOIN z", which is the 
"target" here, can be of many forms.  In the default "polymorphic" case, it's a 
LEFT OUTER JOIN.  If the relationship referred to "Test1" directly, then it 
would be an inner join, and parenthesization would be required given LEFT OUTER 
join from the parent.  If the Test/Test1 mapping were a concrete mapping, it 
wouldn't be a JOIN, it would be a SELECT .. UNION SELECT ... [UNION...], and a 
fully parenthesized subquery would be necessary there as well.    There are 
situations where the *left* side is itself a polymorphic outer join or similar, 
and parenthesization is necessary.

So there's all kinds of combinations that can occur between <parent> <some kind 
of join> <some kind of target selectable>, and SQLAlchemy's current status is 
to treat them all exactly the same - the right side is always wrapped in a 
subquery.  It took years to get this right, but the end result is that 
SQLAlchemy always returns the right result, in a truly enormous number of 
circumstances.  SQLA goes for "the right answer, always" first, performance 
second.

So the issue of "unwrapping" this series when possible is an advanced 
optimization issue.   There are tickets to attempt this in various scenarios, 
to "unwrap" when appropriate, to actually render the joins parenthesized 
directly when SQLite isn't used, but these use cases are considerably 
complicated.   It means the current approach of one way that always works needs 
to be broken down into an additional series of decisionmaking chains, where new 
mistakes and inconsistencies can be introduced.   It's really kind of the next 
level for Query, and we're probably getting close to where we can go there, but 
it's a long road.  Query gets major new architectural changes with each major 
release and we laid a ton of new groundwork in 0.8.

Right now, you can achieve an exact SQL statement using Table instances 
directly.  I wasn't sure if the polymorphic eager loading part would work here, 
but it seems to:


    test = Test.__table__
    test1 = TestOne.__table__

    query = session.query(TestChild).\
                outerjoin(test, test.c.id == TestChild.test_id).\
                outerjoin(test1, test1.c.id == test.c.id).\
                options(contains_eager(TestChild.parent))

    tc = query.filter(TestChild.id == 1).first()

YMMV with more complex scenarios, though.

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to