如何使用sqlalchemy在子句中编写多列

时间:2013-12-09 02:04:49

标签: python mysql sqlalchemy

请建议有没有办法使用SQLAlchemy在子句中编写查询多列?

以下是实际查询的示例:

SELECT  url FROM pages WHERE (url_crc, url) IN ((2752937066, 'http://members.aye.net/~gharris/blog/'), (3799762538, 'http://www.coxandforkum.com/'));

我有一个包含两列主键的表,我希望避免再添加一个键作为索引。

PS我正在使用mysql DB。

更新:此查询将用于批处理 - 因此我需要将几百对放入in子句中。使用IN子句方法,我希望知道我可以在一个查询中绑定多少对的固定限制。就像Oracle默认拥有1000枚枚举限制一样。

使用AND / OR组合可能受chars中查询长度的限制。哪个是可变的,不太可预测。

3 个答案:

答案 0 :(得分:8)

假设您在Page中定义了模型,以下是使用tuple_的示例:

keys = [
    (2752937066, 'http://members.aye.net/~gharris/blog/'),
    (3799762538, 'http://www.coxandforkum.com/')
]

select([
    Page.url
]).select_from(
    Page
).where(
    tuple_(Page.url_crc, Page.url).in_(keys)
)

或者,使用查询API:

session.query(Page.url).filter(tuple_(Page.url_crc, Page.url).in_(keys))

答案 1 :(得分:1)

我不认为这在sqlalchemy中是可行的,并非所有RDMBS都支持这一点 您始终可以将此转换为OR(AND...)条件:

filter_rows = [
    (2752937066, 'http://members.aye.net/~gharris/blog/'),
    (3799762538, 'http://www.coxandforkum.com/'),
    ]
qry = session.query(Page)
qry = qry.filter(or_(*(and_(Page.url_crc == crc, Page.url == url) for crc, url in filter_rows)))
print qry

应该产生类似(对于SQLite):

SELECT  pages.id AS pages_id, pages.url_crc AS pages_url_crc, pages.url AS pages_url
FROM    pages
WHERE   pages.url_crc = ? AND pages.url = ? OR pages.url_crc = ? AND pages.url = ?
-- (2752937066L, 'http://members.aye.net/~gharris/blog/', 3799762538L, 'http://www.coxandforkum.com/')

或者,您可以将两列合并为一个:

filter_rows = [
    (2752937066, 'http://members.aye.net/~gharris/blog/'),
    (3799762538, 'http://www.coxandforkum.com/'),
    ]
qry = session.query(Page)
qry = qry.filter((func.cast(Page.url_crc, String) + '|' + Page.url).in_(["{}|{}".format(*_frow) for _frow in filter_rows]))
print qry

产生以下(对于SQLite),因此您可以使用IN

SELECT  pages.id AS pages_id, pages.url_crc AS pages_url_crc, pages.url AS pages_url
FROM    pages
WHERE   (CAST(pages.url_crc AS VARCHAR) || ? || pages.url) IN (?, ?)
-- ('|', '2752937066|http://members.aye.net/~gharris/blog/', '3799762538|http://www.coxandforkum.com/')

答案 2 :(得分:0)

我最终使用基于test()的解决方案:在((:a1,:b1),(:a2,:b2),...)中生成“(a,b)”,使用命名绑定变量并生成带有绑定变量值的字典。

params = {}
for counter, r in enumerate(records):
    a_param = "a%s" % counter
    params[a_param] = r['a']
    b_param = "b%s" % counter
    params[b_param] = r['b']
    pair_text = "(:%s,:%s)" % (a_param, b_param)
    enum_pairs.append(pair_text)
multicol_in_enumeration = ','.join(enum_pairs)
multicol_in_clause = text(
    " (a,b) in (" + multicol_in_enumeration + ")")
q = session.query(Table.id, Table.a,
                            Table.b).filter(multicol_in_clause).params(params)

我考虑使用mysql upserts的另一个选项,但这会使整个包含在其他db引擎中的可移植性更小,然后使用multicolumn in子句。

更新 SQLAlchemy具有sqlalchemy.sql.expression.tuple_(*clauses, **kw)构造,可用于相同目的。 (我还没有尝试过)