Sqlalchemy from_self во внутреннем запросе, ссылающемся на внешнюю таблицу псевдонимов - PullRequest
0 голосов
/ 23 мая 2018

У меня есть запрос sqlalchemy, подобный этому:

E = aliased(Entity, name='e')
db.session.query(E) \
    .filter(E.user_id == user_id) \
    .filter(
        db.session.query(Entity) \
            .distinct(Entity.original_id) \
            .order_by(Entity.original_id, Entity.id.desc())
            .filter(Entity.ref_id == E.ref_id) \
            .from_self(Entity.flag) \
            .order_by(Entity.timestamp.desc()) \
            .limit(1).as_scalar()
    ) \
    .order_by(E.timestamp) \
    .all()

Он генерирует (примерно) следующий SQL:

SELECT *
FROM entity AS e
WHERE e.user_id = 1 AND (
    SELECT anon_1.entity_flag AS flag
    FROM (
        SELECT DISTINCT ON (entity.original_id)
            entity.flag AS entity_flag, entity.timestamp AS entity_timestamp
        FROM entity, entity AS e # THIS "entity AS e" SHOULD NOT BE HERE
        WHERE
            entity.ref_id = e.ref_id
            ORDER BY entity.original_id, entity.id DESC
    ) AS anon_1
    ORDER BY anon_1.entity_timestamp
    LIMIT 1
) ORDER BY e.timestamp;

Поскольку он каким-то образом добавляет entity AS e во внутренний запрос *Предложение 1008 * заставляет WHERE entity.ref_id = e.ref_id не ссылаться на внешнюю таблицу, как следует.

Почему этот посторонний entity AS e добавляется к внутреннему предложению FROM и как его преодолеть?

1 Ответ

0 голосов
/ 24 мая 2018

Возможно, вы нашли пределы Query.from_self():

Функция автоматического наложения имен работает только в режиме limited ,для простых фильтров и заказов.Более амбициозные конструкции, такие как ссылки на сущность в соединениях, должны предпочитать использовать явные объекты подзапроса, обычно используя метод Query.subquery() для создания явного объекта подзапроса.Всегда проверяйте структуру запросов, просматривая SQL, чтобы убедиться, что конкретная структура выполняет то, что ожидалось!

В общем случае «автокорреляция» SQLAlchemy рассматривает объекты FROM для корреляции только из непосредственного вмещающего запроса и если требуются более глубокие уровни вложенности, должна использоваться явная корреляция.

С другой стороны, в вашем случае это не поможет.Добавление вызова к Query.correlate() по какой-то причине не нарушает границы Query.from_self(), хотя их совместное использование даже упоминается в документации:

Аргументы корреляциивступают в силу в тех случаях, когда используется Query.from_self(), или когда подзапрос, возвращаемый Query.subquery(), встроен в другую конструкцию select().

Решение состоит в использовании явных подзапросов:

In [65]: sq = session.query(Entity.flag, Entity.timestamp).\
    ...:     distinct(Entity.original_id).\
    ...:     order_by(Entity.original_id, Entity.id.desc()).\
    ...:     filter(Entity.ref_id == E.ref_id).\
    ...:     correlate(E).\
    ...:     subquery()

In [66]: q = session.query(E).\
    ...:     filter(E.user_id == 123).\
    ...:     filter(session.query(sq.c.flag).
    ...:            order_by(sq.c.timestamp.desc()).
    ...:            limit(1).
    ...:            as_scalar()).\
    ...:     order_by(E.timestamp)
    ...:            

In [67]: print(q.statement.compile(dialect=postgresql.dialect()))
SELECT e.id, e.ref_id, e.timestamp, e.original_id, e.user_id, e.flag 
FROM entity AS e 
WHERE e.user_id = %(user_id_1)s AND (SELECT anon_1.flag 
FROM (SELECT DISTINCT ON (entity.original_id) entity.flag AS flag, entity.timestamp AS timestamp 
FROM entity 
WHERE entity.ref_id = e.ref_id ORDER BY entity.original_id, entity.id DESC) AS anon_1 ORDER BY anon_1.timestamp DESC 
 LIMIT %(param_1)s) ORDER BY e.timestamp
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...