2014-09-25 93 views
1

我試圖查詢和篩選一對多的關係,似乎無法弄清楚如何做到這一點。過濾SQLAlchemy一對多與「不包含」

這裏是我的映射(修剪爲了簡潔):

class Bug(Base): 
    __tablename__ = 'bug' 
    id = Column('bug_id', Integer, primary_key=True) 
    tags = relationship('Tag', backref='bug') 

class Tag(Base): 
    id = Column('tag_id', Integer, primary_key=True) 
    name = Column('tag_name', String) 
    bug_id = Column('bug_id', ForeignKey('bug.bug_id')) 

我希望能夠找到沒有與名稱爲「foo」標籤的所有錯誤。

回答

3

我不確定Tag表應該代表什麼,但奇怪的是,您的模式將每個Tag與恰好一個Bug關聯起來。如果要使用同名標記標記多個Bug,您將在Tag類中使用相同的name創建多行。這似乎違反了3rd normal form

在數據庫中描述標籤雲的標準方法是使用與關聯(錯誤,標籤)對的輔助「關聯」表的多對多關係。 SQLAlchemy文檔有一個very nice tutorial on this pattern

如果您堅持原樣使用您的模式,有幾種方法可以做到這一點。

客戶端過濾

這顯然效率低下,但很容易理解。你經歷的錯誤一個接一個,通過他們的標籤一個接一個,並消除缺陷,其中tag.name=="foo"

non_foo_bugs = [ bug for bug in session.query(Bug) 
       if not any(tag.name=="foo" for tag in bug.tag) ] 

兩個查詢

找到所有不同的bug 標記爲「富」,然後找到該集合的補充。

這個版本使用的數據庫正好有兩個疑問:讓foo_bugs子查詢

foo_bugs = [t.bug_id for t in session.query(Tag).filter_by(name="foo").distinct()] 
session.query(Bug).filter(~Bug.id.in_(foo_bugs)) 

一個使用子查詢

同上面的查詢,但是,因爲沒有理由在獲取其內容客戶端:

foo_bugs = session.query(Tag.bug_id).filter_by(name="foo").distinct().subquery() 
session.query(Bug).filter(~Bug.id.in_(foo_bugs)) 

這將是一個不相關的子查詢,所以從服務器的角度來看,應該只是對優化與兩個單獨的查詢相同。

+1

您對數據如何映射爲「錯誤」的評估是正確的。我實際上是映射一個現有的數據庫結構,我有隻讀訪問權限,所以我正在做我所擁有的。感謝您提供完整的答案和多種選擇! – Jared 2014-09-26 18:15:34

4

您可以在關係上使用any()運算符。

bugs_without_foo = session.query(Bug).filter(
    db.not_(Bug.tags.any(Tag.name == 'foo')) 
).all() 

這是更好看,但它可能是在非常大的數據集比丹·倫斯基的答案子查詢效率較低。

+0

有趣。 [根據手冊](http://docs.sqlalchemy.org/en/rel_0_9/orm/internals.html#sqlalchemy.orm.properties.RelationshipProperty.Comparator。任何)使用'any()'會產生一個相關的子查詢,這在大多數情況下確實效率較低。但我同意你的觀點,代碼更漂亮,更Pythonic :) – 2014-09-26 00:43:23