1

SQLAlchemy を介して体系化された次のクエリを検討してください。

# Create a CTE that performs a join and gets some values
x_cte = session.query(SomeTable.col1
                     ,OtherTable.col5
                     ) \
               .select_from(SomeTable) \
               .join(OtherTable, SomeTable.col2 == OtherTable.col3)
               .filter(OtherTable.col6 == 34)
               .cte(name='x')

# Create a subquery that splits the CTE based on the value of col1
# and computes the quartile for positive col1 and assigns a dummy
# "quartile" for negative and zero col1
subquery = session.query(x_cte
                        ,literal('-1', sqlalchemy.INTEGER).label('quartile')
                        ) \
                  .filter(x_cte.col1 <= 0)
                  .union_all(session.query(x_cte
                                          ,sqlalchemy.func.ntile(4).over(order_by=x_cte.col1).label('quartile')
                                          )
                                    .filter(x_cte.col1 > 0)
                            ) \
               .subquery()

# Compute some aggregate values for each quartile
result = session.query(sqlalchemy.func.avg(subquery.columns.x_col1)
                      ,sqlalchemy.func.avg(subquery.columns.x_col5)
                      ,subquery.columns.x_quartile
                      ) \
                .group_by(subquery.columns.x_quartile) \
                .all()

長々と申し訳ありませんが、これは私の実際のクエリに似ています。実際のコードでは、CTE にわかりやすい名前を付けました。CTE には、平均を計算する必要がある列がはるかに多くあります。(実際には、CTE の列によって加重された加重平均でもあります。)

本当の「問題」は純粋に、コードをより明確で短くしようとすることです。(はい、わかっています。このクエリはすでにモンスターで読みにくいですが、クライアントはこのデータが利用可能であることを主張しています。) 最後のクエリでは、列をsubquery.columns.x_[column name];として参照する必要があることに注意してください。これは、SQLAlchemy が列名の前に CTE 名を付けているためです。SQLAlchemy が列名を生成するときに CTE の名前を省略したいのですが、多くの列があるため、サブクエリで個別にリストしたくないのです。CTE 名を省略すると、列名 (それ自体で十分な長さ) が短くなり、読みやすくなります。列が一意であることを保証できます。これどうやってするの?

SQLAlchemy 0.7.10 で Python 2.7.3 を使用します。

4

1 に答える 1

1

ここで「x_」が何であるかはあまり具体的ではありませんが、それが最終結果である場合は、 label() を使用して、結果列に任意の名前を付けます。

row = session.query(func.avg(foo).label('foo_avg'), func.avg(bar).label('bar_avg')).first()
foo_avg = row['foo_avg']  # indexed access
bar_avg = row.bar_avg     # attribute access

編集:ここで「x_」を再現できません。ここにテストがあります:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class A(Base):
    __tablename__ = "a"

    id = Column(Integer, primary_key=True)

    x = Column(Integer)
    y = Column(Integer)

s = Session()

subq = s.query(A).cte(name='x')

subq2 = s.query(subq, (subq.c.x + subq.c.y)).filter(A.x == subq.c.x).subquery()

print s.query(A).join(subq2, A.id == subq2.c.id).\
        filter(subq2.c.x == A.x, subq2.c.y == A.y)

subq2.c.<colname>上記では、問題なく参照できることがわかります。「x」が前に付いていません。SQLAlchemy のバージョン情報を指定し、例を完全に記入していただければ、問題を再現するためにそのまま実行できます。

于 2013-03-14T18:25:04.210 に答える