Hello.

Thank you for your prompt answer. I will try to create a working example that
demonstrates the issue. Though it will take me a couple of days, maybe weeks (my
regular work followed by a vacation).

I have another problem. I rephrased the SQL, because postgres's planner had
issues with EXISTS and thought it is a great idea to perform full scans of
several huge tables in order to return several hundreds result rows. Enter 
CTEs...

Short-short intro:
* Client is the joined table inheritance root.
* PersonalClient and CorporateClient are its subclasses.
* Partner is a salesman.
* Deal is a contract signed between salesman(s) and client(s).
* ImportedClientShare and ClientShare are M:N relationships between clients and
deals.
* ImportedPartnerShare and PartnerShare are M:N relationships between partners
and deals.
* We import deals from an external DB. Those are called imported. Imported deal
has external_id and Imported*Share apply.
* However, a user of our system (a partner) can create a new deal locally. Such
a deal does not have an external id (yet) and local *Share apply to it.

The following code should return all clients of a given partner via
ImportedClientShare or via ClientShare:

        q1 = session.query(ImportedClientShare.client_id.label('client_id'))
        q1 = q1.join(ImportedPartnerShare, ImportedClientShare.deal_id ==
ImportedPartnerShare.deal_id)
        q1 = q1.join(Deal, ImportedClientShare.deal_id == Deal.id)
        q1 = q1.filter(
            ImportedPartnerShare.partner_id == partner_id, # input argument
            Deal.external_id != None,
        )
        q2 = session.query(ClientShare.client_id.label('client_id'))
        q2 = q2.join(PartnerShare, ClientShare.deal_id == PartnerShare.deal_id)
        q2 = q2.join(Deal, ClientShare.deal_id == Deal.id)
        q2 = q2.filter(
            PartnerShare.partner_id == partner_id, # input argument
            Deal.external_id == None,
        )
        client_ids = q1.union(q2).cte('client_ids')
        q = session.query(Client).with_polymorphic([PersonalClient,
CorporateClient])
        q = q.join(client_ids, Client.id == client_ids.c.client_id)


It produces the following SQL:

WITH client_ids AS (
        SELECT anon_1.client_id AS client_id
        FROM (
                SELECT imported_client_share.client_id AS client_id
                FROM
                        imported_client_share
                        JOIN imported_partner_share ON 
imported_client_share.deal_id =
imported_partner_share.deal_id
                        JOIN deal ON imported_client_share.deal_id = deal.id
                WHERE
                        imported_partner_share.partner_id = 610556
                        AND deal.external_id IS NOT NULL
                UNION
                SELECT client_share.client_id AS client_id
                FROM
                        client_share
                        JOIN partner_share ON client_share.deal_id = 
partner_share.deal_id
                        JOIN deal ON client_share.deal_id = deal.id
                WHERE
                        partner_share.partner_id = 610556
                        AND deal.external_id IS NULL
        ) AS anon_1
)
SELECT
        client.type AS client_type,
        client.id AS client_id,
        client.external_id AS client_external_id,
        personal_client.id AS personal_client_id,
        personal_client.data_id AS personal_client_data_id,
        corporate_client.id AS corporate_client_id,
        corporate_client.data_id AS corporate_client_data_id
FROM
        client
        LEFT OUTER JOIN personal_client ON client.id = personal_client.id
        LEFT OUTER JOIN corporate_client ON client.id = corporate_client.id
        JOIN client_ids ON client.id = client_ids.client_id


The SQL IS correct, returns expected results when executed in pgadmin3 for
example, but SA's ORM layer crashes with the error:

  File "/usr/lib/python2.7/dist-packages/sqlalchemy/orm/query.py", line 2348, in
instances
    rows = [process[0](row, None) for row in fetch]
  File "/usr/lib/python2.7/dist-packages/sqlalchemy/orm/mapper.py", line 2011,
in _instance
    return _instance(row, result)
  File "/usr/lib/python2.7/dist-packages/sqlalchemy/orm/mapper.py", line 2024,
in _instance
    tuple([row[column] for column in pk_cols])
  File "/usr/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 2835,
in _key_fallback
    expression._string_or_unprintable(key))
NoSuchColumnError: "Could not locate column in row for column 'client.id'"

I also tried it without with_polymorphic() and the result is the same.

Do you have any idea what is going on / what am I doing wrong and how I can fix
this?


Thanks,

Ladislav Lenart


On 27.2.2013 19:05, Michael Bayer wrote:
> 
> On Feb 27, 2013, at 12:21 PM, Ladislav Lenart <lenart...@volny.cz> wrote:
> 
>> Hello.
>>
>> Suppose I have the following mapped classes, A and B, that have two distinct 
>> M:N
>> relationships, AB1 and AB2. If A.x is null, only relations in AB1 apply. If 
>> it
>> is not null, only relations in AB2 apply. A also has 1:N relationship to C 
>> (one
>> A can have more Cs). Finally, A is infact a joined table inheritance 
>> superclass
>> with two subclasses, A1 and A2. I want to select all As for a given B via 
>> AB1 or
>> AB2. I also want to prefetch A.cs of the results using joinedload. I use the
>> code like this:
>>
>> q1 = session.query(A).with_polymorphic([A1, A2])
>> q1 = q1.filter(exists().where(and_(
>>    A.x == None,
>>    AB1.a_id == A.id,
>>    AB1.b_id == b_id, # input argument
>> ))
>>
>> q2 = session.query(A).with_polymorphic([A1, A2])
>> q2 = q2.filter(exists().where(and_(
>>    A.x != None,
>>    AB2.a_id == A.id,
>>    AB2.b_id == b_id, # input argument,
>> ))
>>
>> q = q1.union_all(q2)
>> q = q.options(
>>    joinedload(A.cs),
>> )
>>
>> return q
>>
>> This creates the following SQL:
>>
>> SELECT ....
>> FROM (
>>    SELECT... -- via AB1
>>    UNION ALL
>>    SELECT ... -- via AB2
>> ) anon_1
>> LEFT OUTER JOIN c ON c.a_id = a.id -- error line
>>
>> This fails with a missing from clause error for table a. The attribute a.id 
>> is
>> actually anon_1.a_id.
> 
> I'm kind of amazed it even managed to render that without bombing out a lot 
> sooner.   I'm not sure joinedload() is sophisticated enough right now to 
> figure out the two queries inside of a UNION and all of that, one thing to 
> try would be if your code has different/better/worse behavior on 0.8 (I'd be 
> curious), but short of that the workaround is to join explicitly and then use 
> contains_eager(), like:
> 
> q = q.outerjoin(A.cs).options(contains_eager(A.cs))
> 
> that's assuming plain old outerjoin(A.cs) works here (which it also might 
> have similar issues).    If that's not working then you really might have to 
> get more explicit, in a case this complicated that almost might not be worth 
> it.
> 
> Another option is to use subqueryload(A.cs) instead.  subqueryload() tends to 
> produce a wider range of queries and also tends to put less strain on the 
> database.
> 
> Ultimately its a SQLAlchemy bug but it's not clear how hard it would be to 
> fix.   If all the above options fail I might try to see if there's a repair 
> path at least within 0.8.   A link to working example mappings would be of 
> great help here.
> 
> 
> 
> 
> 
> 


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


Reply via email to