Database wizards,
I've got a situation where I have one table with multiple one-to-many
mappings (i.e., it is the "one" in a "one-to-many" with more than one other
table).
For each row of the "one", I want to group each of the "many"s by some
column and do an outer join between the "one" and the "group by max" rows
of each "many."
Minimal example:
#################################################################################################
from sqlalchemy import create_engine, Column, String, Integer, ForeignKey
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine("sqlite:///:memory:", echo = True)
session = sessionmaker(bind = engine)()
Base = declarative_base()
class A(Base):
__tablename__ = 'A'
id = Column(Integer, primary_key = True)
letter = Column(String)
def __repr__(self):
return "A(id = %s, letter = %s)" % (self.id, self.letter)
class B(Base):
__tablename__ = 'B'
id = Column(Integer, primary_key = True)
A_id = Column(Integer, ForeignKey('A.id'), nullable = False)
word = Column(String)
number = Column(Integer)
def __repr__(self):
return "B(A_id = %s, word = %s, number = %s)" % (self.A_id,
self.word, self.number)
class C(Base):
__tablename__ = 'C'
id = Column(Integer, primary_key = True)
A_id = Column(Integer, ForeignKey('A.id'), nullable = False)
word = Column(String)
number = Column(Integer)
def __repr__(self):
return "B(A_id = %s, word = %s, number = %s)" % (self.A_id,
self.word, self.number)
Base.metadata.create_all(engine)
session.add_all([
A(letter = "A", id = 1),
A(letter = "B", id = 2),
A(letter = "C", id = 3),
])
session.commit()
session.add_all([
B(A_id = 1, word = "Foo", number = 1),
B(A_id = 1, word = "Bar", number = 2),
B(A_id = 3, word = "Baz", number = 2),
])
session.commit()
session.add_all([
C(A_id = 1, word = "cat", number = 5),
C(A_id = 2, word = "dog", number = 6),
C(A_id = 2, word = "cow", number = 2),
])
session.commit()
## I want a query which returns
## A.letter | B.word | B.number | C.word | C.number
## ------------------------------------------------
## A | Bar | 2 | cat | 5
## B | NULL | NULL | dog | 6
## C | Baz | 2 | NULL | NULL
##
## That is, for each A
## 1) Group table B by A_id, and select the B in each group with the
highest B.number
## 1) Group table C by A_id, and select the C in each group with the
highest C.number
## Returning NULL (left outer join) if nothing is found
################################################################################
I've tried playing with subqueries and func.max and I'm able to get the
outer join + grouping, but I'm not getting the "max row" for each group.
Something like this:
###################################################################################
from sqlalchemy import func
b_subq = session.query(func.max(B.number).label("b_max"),
B).group_by(B.A_id).subquery()
c_subq = session.query(func.max(C.number).label("c_max"),
C).group_by(C.A_id).subquery()
session.query(A, B, C).outerjoin(B, C).group_by(A).outerjoin(b_subq,
c_subq).all() ## Fails for some reason
session.query(A, B, C).outerjoin(B, C).outerjoin(b_subq).group_by(A).all()
## Works after removing c_subq
###################################################################################
Any pointers?
Thanks,
Michael
(Details: python 2.7.3, sqlalchemy 0.8.1, RHEL 6)
--
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.