J'essaie d'extraire dans une seule requête un ensemble fixe de lignes, plus quelques autres lignes trouvées par une sous-requête. Mon problème est que la requête générée par mon code SQLAlchemy est incorrecte.Comment spécifier les tables FROM dans les sous-requêtes SQLAlchemy?
Le problème est que la requête générée par SQLAlchemy est comme suit:
SELECT tbl.id AS tbl_id
FROM tbl
WHERE tbl.id IN
(
SELECT t2.id AS t2_id
FROM tbl AS t2, tbl AS t1
WHERE t2.id =
(
SELECT t3.id AS t3_id
FROM tbl AS t3, tbl AS t1
WHERE t3.id < t1.id ORDER BY t3.id DESC LIMIT 1 OFFSET 0
)
AND t1.id IN (4, 8)
)
OR tbl.id IN (0, 8)
alors que la requête correcte ne devrait pas avoir la deuxième tbl AS t1
(le but de cette requête est de sélectionner ID 0 et 8, ainsi comme les ID juste avant 4 et 8).
Malheureusement, je ne peux pas trouver comment obtenir SQLAlchemy pour générer le bon (voir le code ci-dessous).
Des suggestions pour obtenir le même résultat avec une requête plus simple sont également les bienvenues (elles doivent cependant être efficaces - j'ai essayé quelques variantes et certaines étaient beaucoup plus lentes sur mon cas d'utilisation réel).
Le code produisant la requête:
from sqlalchemy import create_engine, or_
from sqlalchemy import Column, Integer, MetaData, Table
from sqlalchemy.orm import sessionmaker
engine = create_engine('sqlite:///:memory:', echo=True)
meta = MetaData(bind=engine)
table = Table('tbl', meta, Column('id', Integer))
session = sessionmaker(bind=engine)()
meta.create_all()
# Insert IDs 0, 2, 4, 6, 8.
i = table.insert()
i.execute(*[dict(id=i) for i in range(0, 10, 2)])
print session.query(table).all()
# output: [(0,), (2,), (4,), (6,), (8,)]
# Subquery of interest: look for the row just before IDs 4 and 8.
sub_query_txt = (
'SELECT t2.id '
'FROM tbl t1, tbl t2 '
'WHERE t2.id = ('
' SELECT t3.id from tbl t3 '
' WHERE t3.id < t1.id '
' ORDER BY t3.id DESC '
' LIMIT 1) '
'AND t1.id IN (4, 8)')
print session.execute(sub_query_txt).fetchall()
# output: [(2,), (6,)]
# Full query of interest: get the rows mentioned above, as well as more rows.
query_txt = (
'SELECT * '
'FROM tbl '
'WHERE ('
' id IN (%s) '
'OR id IN (0, 8))'
) % sub_query_txt
print session.execute(query_txt).fetchall()
# output: [(0,), (2,), (6,), (8,)]
# Attempt at an SQLAlchemy translation (from innermost sub-query to full query).
t1 = table.alias('t1')
t2 = table.alias('t2')
t3 = table.alias('t3')
q1 = session.query(t3.c.id).filter(t3.c.id < t1.c.id).order_by(t3.c.id.desc()).\
limit(1)
q2 = session.query(t2.c.id).filter(t2.c.id == q1, t1.c.id.in_([4, 8]))
q3 = session.query(table).filter(
or_(table.c.id.in_(q2), table.c.id.in_([0, 8])))
print list(q3)
# output: [(0,), (6,), (8,)]
pouvez-vous expliquer un peu plus sur la requête que vous * voulez *; Les questions que vous tentez d'obtenir ne sont pas claires d'après votre question. Est-ce que 'query_text% sub_query_text' renverra les lignes correctes si elles ont été collées dans l'invite de ligne de commande de votre base de données? – SingleNegationElimination
@TokenMacGuy: Le code inclus montre que 'query_text% sub_query_text' renvoie les résultats corrects. La différence est que la sous-requête dans 'sub_query_text' (le plus interne) n'inclut pas de définition pour' t1'; cela fait partie de la requête externe, et cela change le sens. –