2014-08-28 334 views
3

我想將表中的幾個字段組合起來,然後對這些組進行求和,但是他們會被重複計算。在SQLAlchemy中GroupBy和Sum?

我的型號如下:

class CostCenter(db.Model): 
    __tablename__ = 'costcenter' 
    id = db.Column(db.Integer, primary_key=True, autoincrement=True) 
    name = db.Column(db.String) 
    number = db.Column(db.Integer) 

class Expense(db.Model): 

    __tablename__ = 'expense' 
    id = db.Column(db.Integer, primary_key=True, autoincrement=True) 
    glitem_id = db.Column(db.Integer, db.ForeignKey('glitem.id')) 
    glitem = db.relationship('GlItem') 
    costcenter_id = db.Column(db.Integer, db.ForeignKey('costcenter.id')) 
    costcenter = db.relationship('CostCenter') 
    value = db.Column(db.Float) 
    date = db.Column(db.Date) 

我一直在使用:

expenses=db.session.query(Expense,func.sum(Expense.value)).group_by(Expense.date).filter(CostCenter.id.in_([1,2,3])) 

當我打印的費用這是接着的SQL語句。它看起來對我來說是正確的,但我不熟悉SQL。問題是它作爲sum_1輸出的值會被多次計數。如果我在「陳述」中有[1]項,它會將所有三項相加。如果我有[1,2],它會將所有三個相加,然後加倍,如果我有[1,2,3],它將所有三個和三倍相加。我不確定爲什麼它會多次計數。我該如何解決?

SELECT expense.id AS expense_id, expense.glitem_id AS expense_glitem_id, expense.costcenter_id AS   expense_costcenter_id, expense.value AS expense_value, expense.date AS expense_date, sum(expense.value) AS sum_1 
FROM expense, costcenter 
WHERE costcenter.id IN (:id_1, :id_2, :id_3) GROUP BY expense.date 

謝謝!

回答

14

這裏有幾個問題;你似乎沒有在查詢正確的東西。按照Expense.date分組時,選擇Expense對象沒有意義。 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() 

生產這種SQL:

SELECT expense.date AS expense_date, sum(expense.value) AS total 
FROM expense JOIN cost_center ON cost_center.id = expense.cost_center_id 
WHERE cost_center.id IN (?, ?) GROUP BY expense.date 

下面是一個簡單的可運行的例子:

from datetime import datetime 
from sqlalchemy import create_engine, Column, Integer, ForeignKey, Numeric, DateTime, func 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy.orm import Session, relationship 

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)), 
    ]), 
]) 
session.commit() 

base_query = session.query(
    Expense.date, 
    func.sum(Expense.value).label('total') 
).join(Expense.cost_center 
).group_by(Expense.date) 

# first query considers center 1, output: 
# 2014-08-01: 30.00 
# 2014-09-01: 15.00 
for row in base_query.filter(CostCenter.id.in_([1])).all(): 
    print('{}: {}'.format(row.date.date(), row.total)) 

# second query considers centers 1, 2, and 3, output: 
# 2014-07-01: 42.00 
# 2014-08-01: 75.00 
# 2014-09-01: 95.00 
for row in base_query.filter(CostCenter.id.in_([1, 2, 3])).all(): 
    print('{}: {}'.format(row.date.date(), row.total))