2017-01-27 40 views
1

限制我有2類:contains_eager和SQLAlchemy的

class A(Base): 
    id = Column(Integer, primary_key=True) 
    name = Column(String) 
    children = relationship('B') 
class B(Base): 
    id = Column(Integer, primary_key=True) 
    id_a = Column(Integer, ForeignKey('a.id')) 
    name = Column(String) 

現在我需要所有的對象A中含有B與一些名稱和對象將包含過濾所有的B對象。

爲了實現它,我構建了查詢。

query = db.session.query(A).join(B).options(db.contains_eager(A.children)).filter(B.name=='SOME_TEXT') 

現在我只需要50項查詢的,所以我做的:

query.limit(50).all() 

結果包含小於50即使沒有限制有超過50家。我讀預先加載的禪。但是必須有一些技巧來實現它。我的想法之一是做2查詢。一個帶有innerjoin的ID可以在第一個查詢中使用這個ID。

但也許有更好的解決方案。

回答

2

首先,退後一步,看看SQL。您當前的查詢是

SELECT * FROM a JOIN b ON b.id_a = a.id WHERE b.name == '...' LIMIT 50; 

通知的限制是a JOIN b而不是a,但如果你把限制上a您可以通過場b進行過濾。有兩個解決這個問題的方法。第一種方法是使用一個標量子查詢過濾上b.name,像這樣:

SELECT * FROM a 
WHERE EXISTS (SELECT 1 FROM b WHERE b.id_a = a.id AND b.name = '...') 
LIMIT 50; 

這可以根據DB後端是低效的。第二個解決方法是做一個DISTINCT上a的加入後,像這樣:

SELECT DISTINCT a.* FROM a JOIN b ON b.id_a = a.id 
WHERE b.name == '...' 
LIMIT 50; 

請注意,在這兩種情況下,你不會從b得到任何列。我們如何得到它們?再次加入!

SELECT * FROM (
    SELECT DISTINCT a.* FROM a JOIN b ON b.id_a = a.id 
    WHERE b.name == '...' 
    LIMIT 50; 
) a JOIN b ON b.id_a = a.id 
WHERE b.name == '...'; 

現在,寫這一切在SQLAlchemy的:

subquery = (
    session.query(A) 
      .join(B) 
      .with_entities(A) # only select A's columns 
      .filter(B.name == '...') 
      .distinct() 
      .limit(50) 
      .subquery() # convert to subquery 
) 
aliased_A = aliased(A, subquery) 
query = (
    session.query(aliased_A) 
      .join(B) 
      .options(contains_eager(aliased_A.children)) 
      .filter(B.name == "...") 
) 
+0

出於好奇,會使用EXISTS(可能規劃爲半連接),例如遭受什麼DB後端。 –

+0

@IljaEverilä它是那些嚴重依賴多種因素的事情之一,比如數據庫引擎,版本和查詢,所以很難直接給出答案。 *在一般意義上*,連接通常*比子查詢更高效。一如既往,個人資料可以搞清楚。 – univerio

+0

這就是事情,因爲EXISTS可以並且作爲半連接實現,至少在某些dbs中是這樣。當然這可能不適用於所有查詢,但通常發生在這種簡單的情況下。但最後我想你是對的錢:個人資料,看看。 –