2012-02-04 64 views
6

列表爲3元組列表,其中行:如何獲得匹配的3元組的條件與SQLAlchemy的

[(a, b, c), (d, e, f)] 

我想檢索所有從一個表,其中3列中的元組相匹配的行。在這個例子中,查詢WHERE條款可以是這樣的:

(column_X = a AND column_Y = b AND column_Z = c) 
OR (column_X = d AND column_Y = e AND column_Z = f) 

如何創建使用的SQLAlchemy這樣的要求?在我的情況下,三元組列表將包含數百個元素,並且我正在尋找最好的可縮放解決方案。

感謝您的幫助,

回答

8

最簡單的方法將使用的SQLAlchemy提供的tuple_功能:

from sqlalchemy import tuple_ 

session.query(Foo).filter(tuple_(Foo.a, Foo.b, Foo.c).in_(items)) 

這適用於PostgreSQL的,但休息的SQLite。不確定其他數據庫引擎。

幸運的是,有一種解決方法應該適用於所有數據庫。

開始通過與and_表情映射出的所有物品:

conditions = (and_(c1=x, c2=y, c3=z) for (x, y, z) in items) 

然後再創建一個or_過濾器包圍的所有條件:

q.filter(or_(*conditions)) 

這裏有一個簡單的例子:

#/usr/bin/env python 
from sqlalchemy import create_engine 
from sqlalchemy import Column, Integer 
from sqlalchemy.sql import and_, or_ 
from sqlalchemy.orm import sessionmaker 
from sqlalchemy.ext.declarative import declarative_base 

engine = create_engine('sqlite:///') 
session = sessionmaker(bind=engine)() 
Base = declarative_base() 

class Foo(Base): 
    __tablename__ = 'foo' 

    id = Column(Integer, primary_key=True) 
    a = Column(Integer) 
    b = Column(Integer) 
    c = Column(Integer) 

    def __init__(self, a, b, c): 
     self.a = a 
     self.b = b 
     self.c = c 

    def __repr__(self): 
     return '(%d %d %d)' % (self.a, self.b, self.c) 

Base.metadata.create_all(engine) 

session.add_all([Foo(1, 2, 3), Foo(3, 2, 1), Foo(3, 3, 3), Foo(1, 3, 4)]) 
session.commit() 
items = ((1, 2, 3), (3, 3, 3)) 
conditions = (and_(Foo.a==x, Foo.b==y, Foo.c==z) for (x, y, z) in items) 
q = session.query(Foo) 
print q.all() 
q = q.filter(or_(*conditions)) 
print q 
print q.all() 

輸出:

,我懷疑會很好地擴展
$ python test.py 
[(1 2 3), (3 2 1), (3 3 3), (1 3 4)] 
SELECT foo.id AS foo_id, foo.a AS foo_a, foo.b AS foo_b, foo.c AS foo_c 
FROM foo 
WHERE foo.a = :a_1 AND foo.b = :b_1 AND foo.c = :c_1 OR foo.a = :a_2 AND foo.b = :b_2 AND foo.c = :c_2 
[(1 2 3), (3 3 3)] 
+0

非常感謝,它非常完美! – 2012-02-04 11:27:47

2

一個不太常規的方法是創建所有元組的臨時表,然後對加盟:

import sqlalchemy 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy import Column, Integer, Table 
from sqlalchemy.orm import sessionmaker 
Base = declarative_base() 
engine = sqlalchemy.create_engine('sqlite:///:memory:') 
Session = sessionmaker(bind=engine) 
session = Session() 

class Triple(Base): 
    __tablename__ = 'triple' 
    id = Column(Integer(), primary_key=True) 
    x = Column(Integer()) 
    y = Column(Integer()) 
    z = Column(Integer()) 

ws_table = Table('where_sets', Base.metadata, 
     Column('x', Integer()), 
     Column('y', Integer()), 
     Column('z', Integer()), 
     prefixes = ['temporary'] 
    ) 

Base.metadata.create_all(engine) 

... 

where_sets = [(1, 2, 3), (3, 2, 1), (1, 1, 1)] 
ws_table.create(engine, checkfirst=True) 
session.execute(ws_table.insert(), [dict(zip('xyz', s)) for s in where_sets]) 
matches = session.query(Triple).join(ws_table, (Triple.x==ws_table.c.x) & (Triple.y==ws_table.c.y) & (Triple.z==ws_table.c.z)).all() 

其執行這樣的SQL:

INSERT INTO triple (x, y, z) VALUES (?, ?, ?) 
(1, 2, 3) 
INSERT INTO triple (x, y, z) VALUES (?, ?, ?) 
(3, 1, 2) 
INSERT INTO triple (x, y, z) VALUES (?, ?, ?) 
(1, 1, 1) 
SELECT triple.id AS triple_id, triple.x AS triple_x, triple.y AS triple_y, triple.z AS triple_z 
FROM triple JOIN where_sets ON triple.x = where_sets.x AND triple.y = where_sets.y AND triple.z = where_sets.z 
+0

我想這個解決方案可能比前一個更慢,你不覺得嗎?無論如何,謝謝你的例子:-) – 2012-02-04 16:16:35

+0

@Thibaut:直到你嘗試它纔會知道!我所知道的是,我已經將大規模生產系統用巨大的「IN」條款帶到了工作中,我不知道原則上那些巨大的「WHERE」條款會更好。但是你幾乎知道很多INSERT和一個小JOIN會好起來的。既然你特意問了我想到的「最佳可擴展解決方案」,但是也許有幾百個是不夠的,無論如何它都很重要。無論如何,如果你需要的話,它就在那裏。 – 2012-02-04 19:18:14

相關問題