Reputation: 3867
I have an ORM sqlalchemy model, I need to construct a query (that is easier to construct with ORM classes), but it takes a huge amount of time. When I execute the same query directly as SQL to the database, it is quite fast.
Using the profile function provided in SQLAlchemy doc https://docs.sqlalchemy.org/en/latest/faq/performance.html I've found it is indeed the ORM adding a huge overhead.
However, the query result is used in several piece of code, so it would be a headache to change everywhere.
The query looks like this :
q = ( session.query(T1, T2)
.select_from(sub_query)
.join(T1, sub_query.c.id == T1.id)
.join(T2, sub_query.c.id==T2.id)
.filter(T1.user == 1)
.order_by(T1.group, T1.position)
)
The rows are not modified, they are just read using the T1 and T2 attributes. (I mean, I don't need to get a T1 and a T2 object, just something like a raw object with attributes matching T1's and T2's column names)
Is there a way to speed it up (removing ORM overhead) so that I can access them with the same syntax e.g.
for t1, t2 in q.all():
print(t1.x, t2.x)
?
I already tried sqlalchemy.orm.Bundle
, but I get something like "Bundle is not selectable" (certainly due to the join)
Upvotes: 1
Views: 1147
Reputation: 52929
If you wish to continue using SQLAlchemy, but avoid the overhead of the ORM in this particular situation, you can use Bundle
as you've tried:
from sqlalchemy import inspect
from sqlalchemy.orm import Bundle
def to_bundle(cls, label=None):
return Bundle(label or cls.__name__,
*[c.class_attribute for c in inspect(cls).column_attrs])
q = ( session.query(to_bundle(T1), to_bundle(T2))
.select_from(sub_query)
.join(T1, sub_query.c.id == T1.id)
.join(T2, sub_query.c.id==T2.id)
.filter(T1.user == 1)
.order_by(T1.group, T1.position)
)
You should not pass the bundles to Query.join()
, but the mapped classes. The bundles will pick up the columns from the results of the joins. If you do pass the bundle you get the error:
AttributeError: 'Bundle' object has no attribute 'selectable'
Upvotes: 2