你想達到什麼將不會與關係一起工作(這不是一個SA限制,而是處理關係和照顧參照完整性的正確方法)。
然而一個簡單的查詢(包裝在一個方法)將這樣的伎倆就好:
class A(Base):
# ...
def get_children(self, offset, count):
# @todo: might need to handle some border cases
qry = B.query.with_parent(self)
#or: qry = object_session(self).query(B).with_parent(self)
return qry[offset:offset+count]
my_a = session.query(A).get(a_id)
print my_a.get_children(0, 10) # print first 10 children
print my_a.get_children(10, 10) # print second 10 children
編輯-1:實現由只有1-2 SQL語句
現在,在只有1-2個SQL語句中實現這一點絕對有可能。
首先,需要一種方法來獲得的B
的標識符,每個A
。爲此,我們將使用sqlalchemy.sql.expression.over
功能組成一個子查詢:
# @note: this is the subquery using *sqlalchemy.orm.over* function to limit number of rows
# this subquery is used for both queries below
# @note: the code below sorts Bs by id, but you can change it in order_by
subq = (session.query(
B.__table__.c.id.label("b_id"),
over(func.row_number(), partition_by="a_id", order_by="id").label("rownum")
).subquery())
# this produces the following SQL (@note: the RDBMS should support the OVER...)
# >> SELECT b.id AS b_id, row_number() OVER (PARTITION BY a_id ORDER BY id) AS rownum FROM b
版本-1:現在 ,第一個版本將加載A
秒,第二次將加載B
秒。該函數返回A
的字典作爲鍵和B
的名單作爲值:
def get_A_with_Bs_in_batch(b_limit=10):
"""
@return: dict(A, [list of top *b_limit* A.b_s])
@note: uses 2 SQL statements, but does not screw up relationship.
@note: if the relationship is requested via a_instance.b_s, the new SQL statement will be
issued to load *all* related objects
"""
qry_a = session.query(A)
qry_b = (session.query(B)
.join(subq, and_(subq.c.b_id == B.id, subq.c.rownum <= b_limit))
)
a_s = qry_a.all()
b_s = qry_b.all()
res = dict((a, [b for b in b_s if b.a == a]) for a in a_s)
return res
版本2:這將招的SQLAlchemy認爲是裝載在單個查詢與該TOP N Bs
是實際上A.b_s
。非常危險,但很整潔。閱讀代碼解釋件評論:
def get_A_with_Bs_hack_relation(b_limit=10):
"""
@return: dict(A, [list of top *b_limit* A.b_s])
@note: the Bs are loaded as relationship A.b_s, but with the limit.
"""
qry = (session.query(A)
.outerjoin(B)
# @note: next line will trick SA to load joined Bs as if they were *all* objects
# of relationship A.b_s. this is a @hack: and one should discard/reset a session after this
# kind of hacky query!!!
.options(contains_eager(A.b_s))
.outerjoin(subq, and_(subq.c.b_id == B.id, subq.c.rownum <= b_limit))
# @note: next line is required to make both *outerjoins* to play well together
# in order produce the right result
.filter(or_(B.id == None, and_(B.id != None, subq.c.b_id != None)))
)
res = dict((a, a.b_s) for a in qry.all())
return res
總之,版本-2可能是最直接的回答你的問題。因爲你在這裏欺騙SA大時間,如果你以任何方式修改關係屬性,你可能會遇到「Kaboom!」。
你的方法沒問題。謝謝。但我會使用多個查詢來獲得結果。是否可以使用單個SQL來完成這樣的工作?使用此方法的 – flypen 2012-01-08 15:49:10
將只生成單個SQL來完成每次調用的作業。你可以看到這個,如果你通過在你的引擎中設置echo = True來啓用SQL日誌記錄....或者我不理解你的評論? – van 2012-01-08 21:47:35
首先,我將獲得A的集合。如果我不考慮B,那麼我只能使用一個簡單的SQL來完成它。現在我將爲A的每個條目加載一個B集合。然後,我將爲每個A條目發出一個新的SQL。是否可能只使用一個或兩個SQL來完成加載所有數據?從你的解釋來看,也許這很難做到。 – flypen 2012-01-09 05:54:38