2014-12-30 2 views
4

Мы используем SQLAlchemy 0.9.8 на Python 2.7.7 и Postgres 9.3.SQLAlchemy: order_by (Нет) для запросов подзаголовки подключений?

У нас есть запрос, который использует joinloads для полного заполнения некоторых объектов Recipe с использованием одного запроса. Запрос создает большой оператор SQL, который занимает 20 секунд для выполнения - слишком долго. Вот rendered SQL statement on Pastebin.

Представленный SQL имеет предложение ORDER BY, которое объясняет Postgres, является источником 99% времени, затраченного на этот запрос. Похоже, что это связано с отношением в модели ORM, которая имеет предложение order_by.

Однако мы не заботимся о том, чтобы результаты были возвращены для этого запроса - мы заботимся о заказе только при взгляде на один объект. Если я удалю предложение ORDER BY в конце обработанного оператора SQL, запрос выполняется менее чем за секунду - отлично.

Мы попытались использовать .order_by (None) в запросе, но это, кажется, не имеет никакого эффекта. ORDER BY, по-видимому, связан с соединенными нагрузками, потому что если вы меняете одноразовые нагрузки на lazyloads, они уходят. Но нам нужны переменные скорости.

Как я могу заставить SQLAlchemy опустить предложения ORDER BY?


FYI, вот запрос:

missing_recipes = cls.query(session).filter(Recipe.id.in_(missing_recipe_ids)) if missing_recipe_ids else [] 

Вот отрывок из класса ORM:

class Recipe(Base, TransactionalIdMixin, TableCacheMixin, TableCreatedModifiedMixin): 
    __tablename__ = 'recipes' 
     authors = relationship('RecipeAuthor', cascade=OrmCommonClass.OwnedChildCascadeOptions, 
          single_parent=True, 
          lazy='joined', order_by='RecipeAuthor.order', backref='recipe') 
    scanned_photos = relationship(ScannedPhoto, backref='recipe', order_by="ScannedPhoto.position") 
    utensils = relationship(CookingUtensil, secondary=lambda: recipe_cooking_utensils_table) 
    utensil_labels = association_proxy('utensils', 'name') 

Наш запрос() метод выглядит примерно так (еще несколько joinedloads опущены) :

@classmethod 
def query(cls, session): 
    query = query.options(
     joinedload(cls.ingredients).joinedload(RecipeIngredient.ingredient), 
     joinedload(cls.instructions), 
     joinedload(cls.scanned_photos), 
     joinedload(cls.tags), 
     joinedload(cls.authors), 
    ) 

ответ

5

[копирование из моего ответа в списке рассылки]

Вам либо нужно взять order_by от отношения(), возможно, лучшая идея здесь, если заказ не важен, или иначе пропустите joinload(), выпишите сами соединения и используйте contains_eager() (http://docs.sqlalchemy.org/en/rel_0_9/orm/loading_relationships.html?highlight=contains_eager#contains-eager).

joinload() - это макрос, который создает соединения и другие изменения в запросе (например, ORDER BY the relationship), применяет псевдонимы к каждой из этих частей, так что нет никаких шансов, что они будут противоречить чему-либо на запрос, а затем маршрутизирует столбцы из этих дополнительных предложений FROM в коллекции и связанные объекты. contains_eager() делает только самую последнюю часть этого. Первые две части, записывающие объединения и заказы и потенциально их псевдонимы (или нет), зависят от вас в этом случае, поэтому вы сохраняете полный контроль над тем, как выполняется рендеринг запроса.

+0

Мы пробовали обе вещи - выполнение самих стыков было сложным. Мы закончили удаление предложений order_by и сортировку объектов в python в тех местах, которые нам были нужны, отсортированы. Это привело нас к запросу, который занимает 1,5 секунды, почти достаточно быстро. Чтобы идти быстрее, кажется, что нам, вероятно, придется отобразить ответы, которые нужны нам блоку, чтобы избежать накладных расходов на восстановление объектов SQLAlchemy. –

Смежные вопросы