Używanie OR w SQLAlchemy

191

Przejrzałem dokumenty i wydaje mi się, że nie mogę dowiedzieć się, jak wykonać zapytanie OR w SQLAlchemy. Chcę tylko wykonać to zapytanie.

SELECT address FROM addressbook WHERE city='boston' AND (lastname='bulger' OR firstname='whitey')

Powinno być coś takiego

addr = session.query(AddressBook).filter(City == "boston").filter(????)
JiminyCricket
źródło

Odpowiedzi:

322

Z samouczka :

from sqlalchemy import or_
filter(or_(User.name == 'ed', User.name == 'wendy'))
Bastien Léonard
źródło
72
Zauważ, że to podejście obsługuje używanie generatorów, więc jeśli masz długą listę rzeczy do LUB, możesz to zrobićfilter(or_(User.name == v for v in ('Alice', 'Bob', 'Carl')))
robru
66
@ Rada Robru jest niepotrzebnie nieefektywna. Jeśli masz już kolekcję, powinieneś użyć in_operatora w następujący sposób:filter(User.name.in_(['Alice', 'Bob', 'Carl']))
intgr
5
Ach, dzięki, nie wiedziałem, że sqlalchemy ma ten filtr
robru
8
@intgr Przykład pokazany przez robru jest nadal wydajny, jeśli chcesz użyć innego operatora zamiast in_, na przykład operatora LIKE.
Lhassan Baazzi
2
@intgr Moje doświadczenie z Oracle pokazuje, że sekwencja „OR” jest znacznie szybsza niż użycie „IN”. Również „IN” jest ograniczone do zestawu ~ 1000 wpisów, podczas gdy „OR” nie.
ga
321

SQLAlchemy przeciąża operatory bitowe &, |a ~więc zamiast brzydkie i trudne do odczytania z prefiksu składnia or_()i and_()(jak w odpowiedzi na Bastien ) można użyć następujących operatorów:

.filter((AddressBook.lastname == 'bulger') | (AddressBook.firstname == 'whitey'))

Zauważ, że nawiasy nieopcjonalne ze względu na pierwszeństwo operatorów bitowych.

Więc całe zapytanie może wyglądać następująco:

addr = session.query(AddressBook) \
    .filter(AddressBook.city == "boston") \
    .filter((AddressBook.lastname == 'bulger') | (AddressBook.firstname == 'whitey'))
ThiefMaster
źródło
8
+1, ale czy zamiast tego możesz zawinąć dwa ostatnie argumenty filtru w więcej nawiasów i użyć &między nimi a pierwszym (zamiast używać drugiego filterwywołania) dla tego samego efektu?
Chase Sandmann
21
@ChaseSandmann: Tak, możesz. Ale czy byłoby bardziej czytelne? Nie.
ThiefMaster,
1
Byłoby wspaniale mieć link do dokumentów SQLAlchemy tutaj w odpowiedzi!
Cheche
@ ThiefMaster Coincidence, że twój alias zawiera złodzieja i masz Whitey Bulger w swoim przykładzie?
TheRealChx101
35

or_() funkcja może być przydatna w przypadku nieznanej liczby elementów zapytania OR.

Załóżmy na przykład, że tworzymy usługę REST z kilkoma opcjonalnymi filtrami, które powinny zwrócić rekord, jeśli którykolwiek z filtrów zwróci wartość true. Z drugiej strony, jeśli parametr nie został zdefiniowany w zapytaniu, nasze zapytanie nie powinno się zmienić. Bez or_()funkcji musimy zrobić coś takiego:

query = Book.query
if filter.title and filter.author:
    query = query.filter((Book.title.ilike(filter.title))|(Book.author.ilike(filter.author)))
else if filter.title:
    query = query.filter(Book.title.ilike(filter.title))
else if filter.author:
    query = query.filter(Book.author.ilike(filter.author))

Dzięki or_()funkcji można go przepisać do:

query = Book.query
not_null_filters = []
if filter.title:
    not_null_filters.append(Book.title.ilike(filter.title))
if filter.author:
    not_null_filters.append(Book.author.ilike(filter.author))

if len(not_null_filters) > 0:
    query = query.filter(or_(*not_null_filters))
Valar
źródło
1
Bardzo pomocna odpowiedź
Ray Toal
3

To było bardzo pomocne. Oto moja implementacja dla dowolnej tabeli:

def sql_replace(self, tableobject, dictargs):

    #missing check of table object is valid
    primarykeys = [key.name for key in inspect(tableobject).primary_key]

    filterargs = []
    for primkeys in primarykeys:
        if dictargs[primkeys] is not None:
            filterargs.append(getattr(db.RT_eqmtvsdata, primkeys) == dictargs[primkeys])
        else:
            return

    query = select([db.RT_eqmtvsdata]).where(and_(*filterargs))

    if self.r_ExecuteAndErrorChk2(query)[primarykeys[0]] is not None:
        # update
        filter = and_(*filterargs)
        query = tableobject.__table__.update().values(dictargs).where(filter)
        return self.w_ExecuteAndErrorChk2(query)

    else:
        query = tableobject.__table__.insert().values(dictargs)
        return self.w_ExecuteAndErrorChk2(query)

# example usage
inrow = {'eqmtvs_id': eqmtvsid, 'datetime': dtime, 'param_id': paramid}

self.sql_replace(tableobject=db.RT_eqmtvsdata, dictargs=inrow)
delpozow
źródło
Przepraszam, że popełniłem mały błąd, chanhe następującą linię: query = select ([tableobject]). Gdzie (i _ (* filterargs))
delpozov