聚合函数表达式放在`FROM`子句中

Posted

技术标签:

【中文标题】聚合函数表达式放在`FROM`子句中【英文标题】:Aggregate function expression is put in the `FROM` clause 【发布时间】:2018-11-09 14:58:42 【问题描述】:

我正在尝试将查询构建到一个模型 (Loan) 中,通过某些条件过滤并加入另一个模型 (LoanPayment)。

第一个模型与第二个模型具有一对多的关系,我想收集第二个模型的列的聚合值。

模型定义如下:

class Loan(Base):
    __tablename__ = 'loan'
    id = Column(Integer, primary_key=True)
    granted_date = Column(Date, nullable=False)
    state = Column(String(50), default='GRANTED', index=True)
    value = Column(Numeric(19, 10, asdecimal=True),
                   nullable=False, default=money.Decimal(0))
    [... other columns ...]

class LoanPayout(Base):
    __tablename__ = 'loan_payout'
    id = Column(Integer, primary_key=True)
    payment_date = Column(Date, nullable=False)
    value = Column(Numeric(19, 10, asdecimal=True),
                   nullable=False, default=money.Decimal(0))
    loan_id = Column(Integer, ForeignKey('loan.id'),
                     nullable=False, index=True)
    loan = relationship('Loan',
                        backref=backref('payouts', lazy='dynamic',),
                        foreign_keys=[loan_id], order_by=payment_date.asc)
    [... other columns ...]

我想查询所有Loan.granted_date 记录,根据特定条件(比如state='LATE')进行过滤,并返回所有相关支出的sum() 值,按贷款分组。

我尝试了以下 orm 查询:

session.query(
    Loan.id,
    Loan.granted_date,
    func.sum(LoanPayout.principal).alias('loan_payout_total'),
).filter(
    Loan.state == 'LATE',
).join(
    LoanPayout
).group_by(Loan.id)

但 SQLAlchemy(1.2.13 版)正在呈现以下内容,这甚至不是有效的 SQL:

SELECT
    loan.id AS loan_id,
    loan.sale_date AS loan_sale_date,
    loan_payout_total.sum_1 AS loan_payout_total_sum_1
FROM
    sum(loan_payout.principal) AS loan_payout_total,
    loan
JOIN loan_payout
    ON loan.id = loan_payout.loan_id
WHERE
    loan.state = %(state_1)s
GROUP BY
    loan.id

查询几乎是我所期望的:它正确连接了两个表,正确呈现sum() 列并按预期列分组。

但是它将应该是 sum() 列及其别名的内容放在 FROM 部分而不是 SELECT 部分:

FROM
    sum(loan_payout.principal) AS loan_payout_total,
    loan

然后它将一个别名列据称来自那个奇怪的“可选择”放入SELECT 子句:

    loan_payout_total.sum_1 AS loan_payout_total_sum_1

如何使用 ORM 支持让 SQLAlchemy 正确呈现此查询?

【问题讨论】:

相关:***.com/questions/9187530/… 【参考方案1】:

代替

func.sum(LoanPayout.principal).alias('loan_payout_total')

你想要一个label:

func.sum(LoanPayout.principal).label('loan_payout_total')

FunctionElement.alias() 产生一个别名FROM-item,这就是为什么它被放在FROM 子句中。它在例如 Postgresql 中有效。

【讨论】:

谢谢。我可以理解为什么.label().alias() 都可以在func.xxx() 上使用,但我终其一生都无法理解为什么它们不会根据上下文生成正确的代码(@ 987654331@ 与 SELECT) 它实际上有一个逻辑,您将在使用 SQLA 时遇到这种情况。将可选择的事物(表、模型、别名、CTE)作为实体传递给查询构建结构会自动将它们添加到FROM 子句中。 另外,如果生成的别名结构在不同的上下文中表现得完全不同,我个人会觉得它更令人困惑。 SQLA 中的任何地方alias() 用于生成“表”别名,而label() 命名列表达式。

以上是关于聚合函数表达式放在`FROM`子句中的主要内容,如果未能解决你的问题,请参考以下文章

错误:选择列表中的表达式无效(不包含在聚合函数或 GROUP BY 子句中)

s-s-rS 报告在 IF 子句中使用聚合表达式?

Mysql 基础排序查询及常用函数

选择列表中的列无效,因为该列没有包含在聚合函数或 GROUP BY 子句中

sql聚合函数的应用

5.3 进阶3:排序查询