2009-06-04 2 views
6

Я пытаюсь найти способ, чтобы вызвать SQLAlchemy для создания SQL следующего вида:SQLAlchemy пункт комплекса in_

 
select * from t where (a,b) in ((a1,b1),(a2,b2)); 

Возможно ли это?

Если нет, то какие-либо предложения по его эмулированию?

Спасибо любезно!

ответ

3

Хорошо, спасибо Хао Лиану выше, я придумал функциональное, если болезненное решение.

Предположим, что у нас есть декларативный стиль сопоставляются класса, Clazz, и list кортежей составного первичного ключа, values (под редакцией использовать лучше (ИМО) SQL стиль поколения):

 
from sqlalchemy.sql.expression import text,bindparam 
... 
    def __gParams(self, f, vs, ts, bs): 
     for j,v in enumerate(vs): 
      key = f % (j+97) 
      bs.append(bindparam(key, value=v, type_=ts[j])) 
      yield ':%s' % key 

    def __gRows(self, ts, values, bs): 
     for i,vs in enumerate(values): 
      f = '%%c%d' % i 
      yield '(%s)' % ', '.join(self.__gParams(f, vs, ts, bs)) 

    def __gKeys(self, k, ts): 
     for c in k: 
      ts.append(c.type) 
      yield str(c) 

    def __makeSql(self,Clazz, values): 
     t = [] 
     b = [] 
     return text(
       '(%s) in (%s)' % (
        ', '.join(self.__gKeys(Clazz.__table__.primary_key,t)), 
        ', '.join(self.__gRows(t,values,b))), 
       bindparams=b) 

Это решение работает для сложных или простых первичных ключей. Это, вероятно, незначительно медленнее, чем col.in_(keys) для простых первичных ключей.

Я по-прежнему интересуюсь предложениями о лучших способах этого сделать, но этот способ работает до сих пор и работает заметно лучше, чем способ or_(and_(conditions)), или способ for key in keys: do_stuff(q.get(key)).

4

Стандартное предупреждение: я не являюсь экспертом в большой и извилистой экосистеме, которая является SQLAlchemy.

Итак, скажем, у вас есть таблица с именем stocks и сеанс с именем session. Тогда запрос будет просто что-то вроде

x = "(stocks.person, stocks.number) IN ((100, 100), (200, 200))" 
session.query(stocks).filter(x).all() 

Хорошее эмпирическое правило состоит в том, что SQLAlchemy будет принимать сырые SQL в большинстве мест, где он выглядит, как он может быть создан, например, как метод filter.

Однако я не верю, что есть способ сделать это без необработанного SQL. Оператор in_, по-видимому, определен только на Column s, а не на кортежах таких столбцов, как у вас в вашем примере. (Кроме того, это работает только на реализациях SQL, которые его поддерживают - SQLite, в частности, похоже, не поддерживает это в быстрых примерах, которые я запускал. Вы также должны быть осторожны при определении столбцов в левом корте, особенно если SQLAlchemy любезно обработала создание таблицы.)

+0

Я боялся, что - это было бы ужасно полезной конструкцией при работе с составными ключами. Спасибо за ответ! – lostlogic

17

Использование tuple_tuple_ construct:

keys = [(a1, b1), (a2, b2)] 
session.query(T).filter(tuple_(T.a, T.b).in_(keys)).all() 

http://docs.sqlalchemy.org/en/latest/core/sqlelement.html#sqlalchemy.sql.expression.tuple_

+0

Предупреждение Предупреждение (из документа sqlalchemy): композитная конструкция IN не поддерживается всеми бэкэндами, и в настоящее время известно, что она работает на PostgreSQL и MySQL, но не SQLite. Неподдерживаемые бэкенды поднимут подкласс DBAPIError при вызове такого выражения. – Bryan