2009-06-04 11 views
6

Staram się znaleźć sposób, aby spowodować sqlalchemy wygenerować SQL następującą postać:sqlalchemy klauzula kompleks in_

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

Czy to możliwe?

Jeśli nie, jakieś sugestie na temat sposobu naśladowania go?

Dzięki uprzejmie!

Odpowiedz

3

Cóż, dzięki Hao Lianowi powyżej wymyśliłem funkcjonalne, choć bolesne rozwiązanie.

Załóżmy, że mamy deklaratywny stylu odwzorowane klasy, Clazz oraz list krotek związku wartości kluczy podstawowych, values (Zmieniano użyć lepszego (IMO) SQL styl generacji):

 
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) 

To rozwiązanie działa dla złożonych lub prostych kluczy podstawowych. Prawdopodobnie jest on jednak nieco wolniejszy niż col.in_(keys) dla prostych kluczy podstawowych.

Nadal interesują mnie sugestie lepszych sposobów na zrobienie tego, ale ten sposób działa teraz i działa zauważalnie lepiej niż sposób or_(and_(conditions)) lub sposób for key in keys: do_stuff(q.get(key)).

4

Standardowe zastrzeżenie: Nie jestem ekspertem w dużym i krętym ekosystemie, jakim jest SQLAlchemy.

Załóżmy, że masz tabelę o nazwie stocks i sesję o nazwie session. Następnie zapytanie będzie tylko coś

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

Dobrą zasadą jest, że SQLAlchemy zaakceptuje surowego SQL w większości miejsc gdzie to wygląda to może być generowane, takich jak metoda filter.

Jednak nie sądzę, że istnieje sposób, aby to zrobić bez surowego SQL. Operator in_ zdaje się być zdefiniowany tylko na Column s zamiast krotek kolumn, jak masz w swoim przykładzie. (Działa to również tylko na implementacjach SQL, które go obsługują - w szczególności SQLite nie obsługuje tego w szybkich przykładach, które wykonywałem. Musisz również uważać w kwalifikowaniu kolumn w lewej krotce, szczególnie jeśli SQLAlchemy uprzejmie sobie poradził tworzenie tabeli.)

+0

Bałem że - byłoby strasznie użyteczne konstrukt gdy chodzi o złożonych kluczy głównych. Dziękuję za odpowiedź! – lostlogic

17

użytku tuple_:

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

Ostrzeżenie Ostrzeżenie (z dokumentu sqlalchemy): Kompozytowy konstrukt IN nie jest obsługiwany przez wszystkie backendy i obecnie działa w PostgreSQL i MySQL, ale nie w SQLite. Nieobsługiwane backendy podniosą podklasę DBAPIError po wywołaniu takiego wyrażenia. – Bryan