Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sqlalchemy: subquery in FROM must have an alias

How can I structure this sqlalchemy query so that it does the right thing?

I've given everything I can think of an alias, but I'm still getting:

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

Also, as IMSoP pointed out, it seems to be trying to turn it into a cross join, but I just want it to join a table with a group by subquery on that same table.

Here is the sqlalchemy:

(Note: I've rewritten it to be a standalone file that is as complete as possible and can be run from a python shell)

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

generated 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

and here is the toy table:

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

The SQL was I expecting to get (desired SQL) would be something like this:

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

Final note: I'm hoping to get an answer using select() instead of session.query() if possible. Thank you

like image 946
slashdottir Avatar asked Jan 15 '16 02:01

slashdottir


People also ask

Why subquery in FROM must have an alias?

This error usually occurs in development or test environments, as queries written like this will always error out when run, independent of the query plan or values passed in. Recommended Action: Add an alias name of your choice to the subquery in the SQL thats generated by your application.

Can we use alias in subquery?

SQL Correlated Subqueries are used to select data from a table referenced in the outer query. The subquery is known as a correlated because the subquery is related to the outer query. In this type of queries, a table alias (also called a correlation name) must be used to specify which table reference is to be used.

What is subquery in SQLAlchemy?

The grouping is done with the group_by() query method, which takes the column to use for the grouping as an argument, same as the GROUP BY counterpart in SQL. The statement ends by calling subquery() , which tells SQLAlchemy that our intention for this query is to use it inside a bigger query instead of on its own.


1 Answers

You are almost there. Make a "selectable" subquery and join it with the main query via 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__())

Prints (prettified manually):

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

The complete working code is available in this gist.

like image 174
alecxe Avatar answered Oct 15 '22 07:10

alecxe