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 <[email protected]> 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 [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sqlalchemy?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.