У меня проблемы с переводом запроса, созданного в sql (postgres), в sqlalchemy. В частности, мои попытки сопоставления в sqlalchemy приводят к абсурдным рекурсивным результатам, которые будут выполняться гораздо медленнее, чем то, что я изначально написал.
Учитывая следующий тип структуры таблицы:
metadata
------------------------------
primary_id - integer
secondary_count - integer
property - string (many to each primary_id)
data
-----------------------------
primary_id - integer
secondary_id - integer (many to each primary_id)
primary_json - json bytes
secondary_json - json bytes
Я пытаюсь получить пары первичных и вторичных данных с такими данными:
- мы сопоставляем данное свойство
- мы возвращаем только «некоторые» из первичных данных (скажем, 1000)
- мы возвращаем «лучшие» первичные данные, то есть первичные данные с наиболее вторичными данными.
- мы получаем только «некоторые» (скажем, 10) вторичные данные на первичную запись
Первое легко выполнить с помощью объединения двух таблиц, однако второе сложнее. Решение, к которому я обращаюсь (см. здесь для объяснения, которое привело меня к этому решению) в необработанном SQL:
SELECT primary_id, primary_json, secondary_json, secondary_count
FROM
(
SELECT primary_id, secondary_count
FROM metadata
WHERE property='whatever I want'
-- Get the "best" 1000 results
ORDER BY secondary_count DESC
LIMIT 1000
) my_primary_ids
LEFT OUTER JOIN LATERAL
(
SELECT primary_json, seondary_json
FROM data
WHERE primary_id = my_primary_ids.primary_id
-- Only return 10 pieces of secondary json per primary json
LIMIT 10
) json_content ON true;
Я изо всех сил пытался преобразовать это в sqlalchemy, однако у меня по-прежнему возникает проблема, заключающаяся в том, что результирующий запрос перезаписывает подзапрос в предложении FROM
запроса бокового соединения.
Например, код sqlalchemy (при условии, что приведенные ниже определения объектов таблиц соответствуют приведенному выше) является частичным решением. Я думаю, что я могу добавить недостающие столбцы (как вы увидите в сгенерированном sql):
from sqlalchemy import true
my_prim_ids_al = (
query(Metadata.primary_id.label('primary_id'),
Metadata.secondary_count.label('secondary_count'))
.filter_by(property='whatever I want')
.order_by(Metadata.secondary_count)
.limit(1000)
.from_self()
.subquery('my_primary_ids')
)
json_content_al = (
query(Data.primary_json.label('primary_json'),
Data.secondary_json.label('secondary_json'))
.filter_by(primary_id=my_primary_ids_al.c.primary_id)
.limit(10)
.from_self()
.subquery('json_content')
.lateral()
)
joined_query = (
my_primary_ids_al
.outerjoin(json_content_al, true())
.subquery('joined_query')
)
Объединенный запрос в расширенной форме выглядит следующим образом с вышеупомянутой нелепой вложенной структурой:
SELECT anon_1.primary_id, anon_1.secondary_count
FROM
(
SELECT metadata.primary_id AS primary_id,
metadata.secondary_count AS secondary_count
FROM metadata
WHERE metadata.property = 'whatever I want'
ORDER BY metadata.secondary_count DESC
LIMIT :param_1
) AS anon_1
LEFT OUTER JOIN LATERAL
(
SELECT anon_4.anon_3_secondary_json AS anon_3_secondary_json,
anon_4.anon_3_primary_json AS anon_3_primary_json,
FROM
(
SELECT anon_3.secondary_json AS anon_3_secondary_json,
anon_3.primary_json AS anon_3_primary_json,
FROM
(
SELECT data.secondary_json AS secondary_json,
data.primary_json AS primary_json,
FROM data
JOIN
(
SELECT anon_1.primary_id AS primary_id,
anon_1.secondary_count AS secondary_count
FROM
(
SELECT metadata.primary_id AS primary_id,
metadata.secondary_count AS secondary_count
FROM metadata
WHERE metadata.property = 'whatever I want'
ORDER BY metadata.secondary_count DESC
LIMIT :param_1
) AS anon_1
) AS primary_ids ON data.primary_id = primary_ides.primary_id
) AS anon_3
LIMIT :param_2) AS anon_4) AS anon_2 ON true
Опять же, я понимаю, что это неполная попытка, поскольку не все столбцы ВЫБРАТЬСЯ в начале, но ключевая проблема заключается в том, что sqlalchemy создает абсурдное количество вложенных запросов в подзапросе бокового соединения. . Это основная проблема, которую мне не удалось решить, и до тех пор, пока она не будет решена, нет смысла завершать оставшуюся часть запроса.