2016-01-15 2 views
4

Как я могу структурировать этот запрос sqlalchemy так, чтобы он поступал правильно?Sqlalchemy: подзапрос в FROM должен иметь псевдоним

Я дал все, что я могу думать псевдоним, но я все еще получаю:

ProgrammingError: (psycopg2.ProgrammingError) subquery in FROM must have an alias 
LINE 4: FROM (SELECT foo.id AS foo_id, foo.version AS ... 

Кроме того, как IMSoP отметил, что, кажется, пытается превратить его в крест присоединиться, но я просто хочу, чтобы он присоединился к таблице с группой по подзапросу в той же таблице.

Вот SQLAlchemy:

(Примечание: Я переписать его, чтобы быть автономный файл, который является как можно более полным и может быть запущен из питона оболочки)

from sqlalchemy import create_engine, func, select 
from sqlalchemy import Column, BigInteger, DateTime, Integer, String, SmallInteger 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy.orm import sessionmaker 

engine = create_engine('postgresql://postgres:#######@localhost:5435/foo1234') 
session = sessionmaker() 
session.configure(bind=engine) 
session = session() 

Base = declarative_base() 

class Foo(Base): 
    __tablename__ = 'foo' 
    __table_args__ = {'schema': 'public'} 
    id = Column('id', BigInteger, primary_key=True) 
    time = Column('time', DateTime(timezone=True)) 
    version = Column('version', String) 
    revision = Column('revision', SmallInteger) 

foo_max_time_q = select([ 
    func.max(Foo.time).label('foo_max_time'), 
    Foo.id.label('foo_id') 
]).group_by(Foo.id 
).alias('foo_max_time_q') 

foo_q = select([ 
    Foo.id.label('foo_id'), 
    Foo.version.label('foo_version'), 
    Foo.revision.label('foo_revision'), 
    foo_max_time_q.c.foo_max_time.label('foo_max_time') 
]).join(foo_max_time_q, foo_max_time_q.c.foo_id == Foo.id 
).alias('foo_q') 

thing = session.query(foo_q).all() 
print thing 

генерируемый SQL:

SELECT foo_id AS foo_id, 
    foo_version AS foo_version, 
    foo_revision AS foo_revision, 
    foo_max_time AS foo_max_time, 
    foo_max_time_q.foo_max_time AS foo_max_time_q_foo_max_time, 
    foo_max_time_q.foo_id AS foo_max_time_q_foo_id 
    FROM (SELECT id AS foo_id, 
     version AS foo_version, 
     revision AS foo_revision, 
     foo_max_time_q.foo_max_time AS foo_max_time 
     FROM (SELECT max(time) AS foo_max_time, 
      id AS foo_id GROUP BY id 
     ) AS foo_max_time_q) 
    JOIN (SELECT max(time) AS foo_max_time, 
      id AS foo_id GROUP BY id 
    ) AS foo_max_time_q 
    ON foo_max_time_q.foo_id = id 

и вот т ой таблице:

CREATE TABLE foo (
id bigint , 
time timestamp with time zone, 
version character varying(32), 
revision smallint 
); 

SQL, была я ожидал получить (желательно SQL) будет что-то вроде этого:

SELECT foo.id AS foo_id, 
     foo.version AS foo_version, 
     foo.revision AS foo_revision, 
     foo_max_time_q.foo_max_time AS foo_max_time 
     FROM foo 
     JOIN (SELECT max(time) AS foo_max_time, 
      id AS foo_id GROUP BY id 
      ) AS foo_max_time_q 
     ON foo_max_time_q.foo_id = foo.id 

Конечная нота: Я надеюсь получить ответ с помощью select() вместо session.query(), если это возможно. Спасибо

+0

Этот SQL кажется неполным/некорректным в некотором роде - он имеет больше ')', чем '('. Однако подзапрос, который я вижу пропущенный псевдоним, начинается в строке 7 - 'FROM (SELECT foo.id AS foo_id, '- и заканчивается на строке 17 - единственный') '. – IMSoP

+0

Если посмотреть на это, я думаю, что подзапрос был создан SQLAlchemy, потому что он интерпретировал ваш запрос как подразумеваемое перекрестное соединение между' foo' и 'foo_max_time_q' (' FROM foo, (...) как foo_max_time_q') *, а также * ваша явная спецификация соединения ('JOIN (...) AS foo_max_time_q ON foo_max_time_q.foo_id = foo.id'). – IMSoP

+0

@IMSoP : Вот как это порождается. Вот и вся проблема – slashdottir

ответ

9

Вы почти находитесь. Сделать "selectable" подзапрос и присоединиться к нему с помощью основного запроса join():

foo_max_time_q = select([func.max(Foo.time).label('foo_max_time'), 
         Foo.id.label('foo_id') 
         ]).group_by(Foo.id 
         ).alias("foo_max_time_q") 

foo_q = session.query(
      Foo.id.label('foo_id'), 
      Foo.version.label('foo_version'), 
      Foo.revision.label('foo_revision'), 
      foo_max_time_q.c.foo_max_time.label('foo_max_time') 
       ).join(foo_max_time_q, 
         foo_max_time_q.c.foo_id == Foo.id) 

print(foo_q.__str__()) 

Печать (prettified вручную):

SELECT 
    foo.id AS foo_id, 
    foo.version AS foo_version, 
    foo.revision AS foo_revision, 
    foo_max_time_q.foo_max_time AS foo_max_time 
FROM 
    foo 
JOIN 
    (SELECT 
     max(foo.time) AS foo_max_time, 
     foo.id AS foo_id 
    FROM 
     foo 
    GROUP BY foo.id) AS foo_max_time_q 
ON 
    foo_max_time_q.foo_id = foo.id 

Полный рабочий код доступен в этом gist.

+0

Это так здорово. Спасибо. Он все еще полагается на session.query, но я возьму его. У меня возникли проблемы с пониманием того, почему это так отличается. То, что я хотел бы сделать, - объявить весь столбец foo_max_time как отдельный объект, который я могу просто зайти. – slashdottir

+0

. Единственное различие заключается в том, что foo_q выполняется с помощью session.query вместо select? Это кажется мне очень тонким изменением, которое сейчас не интуитивно для меня. – slashdottir

+0

Хорошо, я протестировал его, и он работает. Это улучшит производительность для меня. Большое спасибо – slashdottir

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