Как написать несколько столбцов в предложении с помощью 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. В подход предложения я надеюсь знать фиксированный предел того, сколько пар я могу вставить в один запрос. Как Oracle имеет 1000 enum limit по умолчанию.

использование и / или комбинация могут быть ограничены длиной запроса в символах. Что было бы изменчивым и менее предсказуемым.

3 ответов


предполагая, что ваша модель определена в 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))

Я не думаю, что это в настоящее время возможно в 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/')

Я в конечном итоге с помощью теста() решение: создается "(а,б) ((А1, В1), (А2, В2), ...) "с именем bind vars и генерацией словаря со значениями bind vars.

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, но это сделало бы все включено еще менее портативным для другого движка БД, а затем с помощью предложения multicolumn in.

обновление SQLAlchemy имеет С SQLAlchemy.язык SQL.выражение.tuple_ (*пункты, * * kw) строительство это может быть использовано для той же цели. (Я еще не пробовал)