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 を使用します。