【问题标题】:How to get rows which match a list of 3-tuples conditions with SQLAlchemy如何使用 SQLAlchemy 获取与三元组条件列表匹配的行
【发布时间】:2012-02-26 17:48:04
【问题描述】:

有一个三元组列表:

[(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 创建这样的请求?在我的例子中,三元组列表将包含数百个元素,我正在寻找最佳的可扩展解决方案。

感谢您的帮助,

【问题讨论】:

    标签: python sql sqlalchemy


    【解决方案1】:

    最简单的方法是使用 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)]
    

    【讨论】:

    • 非常感谢,太完美了!
    • 从 1.36 开始更新,sqlite 支持它
    【解决方案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
    

    【讨论】:

    • 我想这个解决方案可能比前一个解决方案慢得多,你不觉得吗?无论如何,谢谢你的例子:-)
    • @Thibaut: 直到你尝试才知道!我所知道的是,我已经用巨大的“IN”子句使大规模生产系统陷入困境,而且我不知道在原则上巨大的“WHERE”子句会更好。但是你几乎知道很多 INSERT 和一个小的 JOIN 就可以了。既然您专门要求“最佳可扩展解决方案”,这就是我的想法,但也许几百个还不够,无论如何它都会很重要。无论如何,如果您需要,它就在那里。
    • 我不会测试 bc 我将在小范围内使用它,但这似乎比巨大的 AND 和 OR 以及 WHERE 和 IN 更具可扩展性。 INSERT 和 JOIN 通常非常快,除非插入大量 od 数据。
    【解决方案3】:

    有人会考虑在原始表中创建一个额外的键吗? 即用“1”-“2”-“3”而不是另一个表创建一个新列并检查唯一性。

    【讨论】:

    • 我的意思是添加一个索引
    猜你喜欢
    • 1970-01-01
    • 2016-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-24
    相关资源
    最近更新 更多