SQLAlchemy从子查询中选择两个连接

时间:2017-03-18 05:46:18

标签: sqlalchemy

我几乎想出了如何将此查询翻译成SQLAlchemy

select u.* from (select
                    unit_id,
                    activity,
                    max(occurred_at) maxOA
                  from Activity
                    group by unit_id) a1

inner join Activity a2 on
    a2.unit_id = a1.unit_id and a2.occurred_at = a1.maxOA

inner join Unit u on
    u.id = a2.unit_id where a2.activity = 'activateReq';

我的表看起来像这样(gDB是保存我的SQLAlchemy数据库连接的全局变量):

class Unit(gDB.Model):
    __tablename__ = 'Unit'
    id = gDB.Column('id', gDB.Integer(), primary_key=True)
    owner_id = gDB.Column('owner_id', gDB.ForeignKey('User.id'))
    owner = gDB.relationship(User, primaryjoin=owner_id == User.id)
    unitNumber = gDB.Column('unit_number', gDB.Integer())
    street = gDB.Column('street', gDB.String(255))
    city = gDB.Column('city', gDB.String(255))
    state = gDB.Column('state', gDB.String(2))
    zip = gDB.Column('zip', gDB.String(9))
    serviceInstallDate = gDB.Column('service_install_date', gDB.DateTime())
    activity = gDB.relationship('Activity', backref='unit_id', lazy='select', order_by="desc(Activity.occurredAt)")

    def __repr__(self):
        return '<Unit: {0} {1}>'.format(self.unitNumber, self.street)

class Activity(gDB.Model):
    __tablename__ = 'Activity'
    id = gDB.Column('id', gDB.Integer(), primary_key=True)
    unitID = gDB.Column('unit_id', gDB.ForeignKey('Unit.id'))
    unit = gDB.relationship(Unit, primaryjoin=unitID == Unit.id)
    agentID = gDB.Column('agent_id', gDB.ForeignKey('User.id'))
    agent = gDB.relationship(User, primaryjoin=agentID == User.id)
    occurredAt = gDB.Column('occurred_at', gDB.DateTime())
    activity = gDB.Column('activity', gDB.String(32))
    notes = gDB.Column('notes', gDB.String())

    def __repr__(self):
        return '<Activity: {0} {1}>'.format(self.activity, self.occurredAt)

我已经走到这一步了:

db.session.query(Activity.unitID, Activity.activity, func.max(Activity.occurredAt).label("maxOA"))
    .group_by(Activity.unitID)
    .subquery()

a2 = expression.alias(Activity)
u = expression.alias(Unit)

q = db.session.query(a1)
    .join(a2, and_(a2.c.unit_id == a1.c.unit_id, a2.c.occurred_at == a1.c.maxOA))
    .join(u, u.c.id == a2.c.unit_id).filter(a2.c.activity == 'activateReq')

q.all()

q.all()正在返回正确的行集,但我想要Unit的列,而不是子查询a1的列。

更新

我已经接近了:诀窍是query()所需的列(在本例中为u的所有列),然后select_from()子查询a1 }:

q = db.session.query(u).select_from(a1).join(a2, and_(a2.c.unit_id == a1.c.unit_id, a2.c.occurred_at == a1.c.maxOA)).join(u, u.c.id == a2.c.unit_id).filter(a2.c.activity=='activateReq')

现在的问题是生成的Unit个实例没有相关的Userowner_id / owner个)对象;我的Jinja模板尝试引用user.owner,我得到了

'sqlalchemy.util._collections.result object' has no attribute 'owner'

我也不知道这是否是最简洁或最有效的方式。

1 个答案:

答案 0 :(得分:1)

啊!我想到了。我使用expression.alias()代替orm.aliased()。它应该是这样的:

a1 = gDB.session.query(Activity.unitID, Activity.activity, func.max(Activity.occurredAt).label("maxOA")) \
    .group_by(Activity.unitID) \
    .subquery()
a2 = aliased(Activity, name="a2")
u = aliased(Unit, name="u")
q = gDB.session.query(u).select_from(a1) \
    .join(a2, and_(a2.unitID == a1.c.unit_id, a2.occurredAt == a1.c.maxOA)) \
    .join(u, u.id == a2.unitID).filter(a2.activity == 'activateReq')