SQLAlchemy 中使用 GroupBy 和 Sum 导致重复计数的问题及解决方法

在 SQLAlchemy 中使用 GroupBySum 时,有时会遇到重复计数或意外的查询结果。这通常是因为在聚合查询中没有正确地指定聚合函数或 GroupBy 条件,导致结果集没有按预期方式分组。

1、问题背景

在使用 SQLAlchemy 进行数据查询时,用户在尝试对表中的几个字段进行分组并对各组进行求和时遇到问题。然而,在实际查询结果中,求和结果被重复计数,导致不正确的数据。

2、解决方案

为了解决这个问题,需要对查询语句进行调整。具体来说,原始的查询语句中存在一些问题:

  1. 在对 Expense 表进行分组时,同时选择了 Expense 对象,这没有意义。
  2. CostCenter 和 Expense 表之间缺少连接条件,导致行被重复,每个成本中心都进行计数,但没有关系。

因此,需要将查询语句修改为如下形式:

复制代码
session.query(
    Expense.date,
    func.sum(Expense.value).label('total')
).join(Expense.cost_center
).filter(CostCenter.id.in_([2, 3])
).group_by(Expense.date
).all()

修改后的查询语句具有以下特点:

  1. 只选择 Expense.date 和求和结果 total,避免了重复选择 Expense 对象。
  2. 使用 join() 方法连接 CostCenter 和 Expense 表,确保只对相关行进行分组和求和。
  3. 使用 filter() 方法过滤出符合条件的 CostCenter,指定了要考虑的成本中心 ID。
  4. 使用 group_by() 方法对 Expense.date 进行分组,以便对每组进行求和。

通过这些修改,可以正确地对数据进行分组和求和,避免了重复计数的问题。

代码示例

以下是一个简单的可运行示例,演示了如何使用修改后的查询语句进行数据分组和求和:

python 复制代码
from datetime import datetime
from sqlalchemy import create_engine, Column, Integer, ForeignKey, Numeric, DateTime, func
from sqlalchemy.orm import Session, relationship
from sqlalchemy.ext.declarative import declarative_base

# 创建引擎和会话
engine = create_engine('sqlite://', echo=True)
session = Session(bind=engine)

# 定义表结构
Base = declarative_base(bind=engine)

class CostCenter(Base):
    __tablename__ = 'cost_center'

    id = Column(Integer, primary_key=True)

class Expense(Base):
    __tablename__ = 'expense'

    id = Column(Integer, primary_key=True)
    cost_center_id = Column(Integer, ForeignKey(CostCenter.id), nullable=False)
    value = Column(Numeric(8, 2), nullable=False, default=0)
    date = Column(DateTime, nullable=False)

    cost_center = relationship(CostCenter, backref='expenses')

# 创建表
Base.metadata.create_all()

# 添加数据
session.add_all([
    CostCenter(expenses=[
        Expense(value=10, date=datetime(2014, 8, 1)),
        Expense(value=20, date=datetime(2014, 8, 1)),
        Expense(value=15, date=datetime(2014, 9, 1)),
    ]),
    CostCenter(expenses=[
        Expense(value=45, date=datetime(2014, 8, 1)),
        Expense(value=40, date=datetime(2014, 9, 1)),
        Expense(value=40, date=datetime(2014, 9, 1)),
    ]),
    CostCenter(expenses=[
        Expense(value=42, date=datetime(2014, 7, 1)),
    ]),
])

# 执行查询
base_query = session.query(
    Expense.date,
    func.sum(Expense.value).label('total')
).join(Expense.cost_center
).group_by(Expense.date)

# 第一次查询,考虑成本中心 1
for row in base_query.filter(CostCenter.id.in_([1])).all():
    print('{}: {}'.format(row.date.date(), row.total))

# 第二次查询,考虑成本中心 1、2 和 3
for row in base_query.filter(CostCenter.id.in_([1, 2, 3])).all():
    print('{}: {}'.format(row.date.date(), row.total))

在执行查询后,它将按日期对数据进行分组并求和,并显示每个组的日期和总和。这样,可以正确地获取数据分组和求和的结果,避免重复计数的问题。

在使用 SQLAlchemy 的 GroupBySum 时,可能会遇到重复计数的问题。通常,这是由于没有正确处理聚合逻辑或缺乏对 SQL 查询的深入理解。通过正确使用聚合函数,并在需要时检查生成的 SQL 语句,可以避免这些问题,确保查询结果的准确性。

相关推荐
萧曵 丶3 分钟前
事务ACID特性详解
数据库·事务·acid
田里的水稻5 分钟前
C++_python_相互之间的包含调用方法
c++·chrome·python
2501_9418705610 分钟前
面向微服务熔断与流量削峰策略的互联网系统稳定性设计与多语言工程实践分享
开发语言·python
modelmd15 分钟前
Go 编程语言指南 练习题目分享
开发语言·学习·golang
fox_mt16 分钟前
AI Coding - ClaudeCode使用指南
java·ai编程
带土132 分钟前
4. C++ static关键字
开发语言·c++
毕设源码-郭学长33 分钟前
【开题答辩全过程】以 基于SSM的高校运动会管理系统的设计与实现为例,包含答辩的问题和答案
java·eclipse
qq_54702617935 分钟前
Maven 使用指南
java·maven
C++ 老炮儿的技术栈39 分钟前
什么是通信规约
开发语言·数据结构·c++·windows·算法·安全·链表
kejiayuan41 分钟前
CTE更易懂的SQL风格
数据库·sql