@toobinks

SQlAlchemy. Как оптимизировать запросы?

Здравствуйте.

Не получается сократить время запроса.
Есть 3 таблицы. Контекст, запрос, точка. В контексте может быть много запросов, а в запросе может быть много точек. Самая объёмная таблица - точка.
Требуется для каждого контекста найти сумму точек. Т.е. для каждого контекста перебрать его запросы и подсчитать количество точек, принадлежащих этим запросам, и сложить.

Как дело обстоит сейчас. Ищутся все контексты и выводятся в таблицу (html), для столбца суммы точек для каждого контекста отдельно создаётся свой запрос по поиску суммы точек, что плохо сказывается на скорости. С увеличением количества точек общее время запрос (и отображения страницы) сильно увеличивается.

# Model
class Context(Model):

    requests = relationship(Request, backref="context")

class Request(Model):

    context_id = db.Column(db.ForeignKey('context.id'))
    points = relationship('Point', backref='requests', lazy='dynamic')

class Point(Model):

    request_id = db.Column(db.ForeignKey('requests.id'))

class Context(Model):

	def get_point_count(self):
	    subquery = Request.query \
	        .with_entities(Request.id) \
	        .with_parent(self) \
	        .subquery()

	    count = Point.query \
	        .join(Request, Point.requests) \
	        .filter(Request.id.in_(subquery)) \
	        .distinct(Point.attr) \
	        .count()
	    return count

# View

query = Context.query
query = query.with_entities(func.max(Context.id))
query = query.group_by(Context.user_id)
subquery = query.subquery()

query = query.filter(Context.id.in_(subquery))
query = query.order_by(Context.id.desc())
contexts = query.all() # ~200ms

# в таблице отображаю контексты и для каждого контекста показываю количество точек
for ctx in contexts:
	ctx.get_point_count() # ~20ms

# Если контекстов на странице 20, то соответственно к основному запросу добавляется 20 запросов, что увеличивает общее время на ~400ms


Как более правильно решить эту задачу?

Спасибо.
  • Вопрос задан
  • 990 просмотров
Решения вопроса 1
Посоветую вам начать с включения SQLALCHEMY_ECHO и просмотра плана результирующего запроса.
Как быстрый совет - ограничивайте запрос данными, которые вам нужны, и не бойтесь подзапросов. В большей части случаев бэкенд БД сделает всю оптимизацию за вас.
result = Context.query.join(
    Request, Request.context_id == Context.id_
).join(
    Point, Point.request_id == Request.id_
).group_by(Context.id_).with_entities(
    Context.field1, ...
    func.count(Point.id_).label('points_count')
)
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы