Frage

Ich habe eine Abfolge von IDs, die ich abrufen möchte. Es ist einfach:

session.query(Record).filter(Record.id.in_(seq)).all()

Gibt es einen besseren Weg, dies zu tun?

War es hilfreich?

Lösung

Ihr Code ist absolut fein.

IN ist wie ein Haufen von X=Y verbunden mit OR und ist in zeitgenössischen Datenbanken ziemlich schnell.

Wenn Ihre IDS-Liste jedoch lang ist, können Sie die Abfrage etwas effizienter gestalten, indem Sie eine Unterablage übergeben, die die Liste der IDs zurückgibt.

Andere Tipps

Der Code ist völlig in Ordnung. Jemand bittet mich jedoch nach einem Absicherungssystem zwischen den beiden Ansätzen, um ein großes in vs. Get () für einzelne IDs zu tun.

Wenn jemand wirklich versucht, die Auswahl zu vermeiden, können Sie am besten die Objekte einrichten, die Sie im Speicher benötigen. Sie arbeiten an einem großen Elementtisch. Teilen Sie die Arbeiten in Brocken auf, wie z. B. die vollständige Arbeiten nach dem Primärschlüssel oder nach dem Datumsbereich, was auch immer, und laden Sie alles für diesen Chunk lokal in einen Cache:

 all_ids = [<huge list of ids>]

 all_ids.sort()
 while all_ids:
     chunk = all_ids[0:1000]

     # bonus exercise!  Throw each chunk into a multiprocessing.pool()!
     all_ids = all_ids[1000:]

     my_cache = dict(
           Session.query(Record.id, Record).filter(
                 Record.id.between(chunk[0], chunk[-1]))
     )

     for id_ in chunk:
         my_obj = my_cache[id_]
         <work on my_obj>

Das ist der reale Anwendungsfall.

Um jedoch auch eine SQLalchemy -API zu veranschaulichen, können wir eine Funktion erstellen, die die in den Aufzeichnungen, die wir nicht haben, und ein lokales Get für diejenigen, die wir tun. Hier ist das:

from sqlalchemy import inspect


def get_all(session, cls, seq):
    mapper = inspect(cls)
    lookup = set()
    for ident in seq:
        key = mapper.identity_key_from_primary_key((ident, ))
        if key in session.identity_map:
            yield session.identity_map[key]
        else:
            lookup.add(ident)
    if lookup:
        for obj in session.query(cls).filter(cls.id.in_(lookup)):
            yield obj

Hier ist eine Demonstration:

from sqlalchemy import Column, Integer, create_engine, String
from sqlalchemy.orm import Session
from sqlalchemy.ext.declarative import declarative_base
import random

Base = declarative_base()


class A(Base):
    __tablename__ = 'a'
    id = Column(Integer, primary_key=True)
    data = Column(String)

e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)

ids = range(1, 50)

s = Session(e)
s.add_all([A(id=i, data='a%d' % i) for i in ids])
s.commit()
s.close()

already_loaded = s.query(A).filter(A.id.in_(random.sample(ids, 10))).all()

assert len(s.identity_map) == 10

to_load = set(random.sample(ids, 25))
all_ = list(get_all(s, A, to_load))

assert set(x.id for x in all_) == to_load

Wenn Sie zusammengesetzte Primärschlüssel verwenden, können Sie verwenden tuple_, wie in

from sqlalchemy import tuple_
session.query(Record).filter(tuple_(Record.id1, Record.id2).in_(seq)).all()

Beachten Sie, dass dies auf SQLite nicht verfügbar ist (siehe Dokument).

Ich würde empfehlen, einen Blick auf die SQL zu werfen, die es produziert. Sie können nur STR (Abfrage) drucken, um es zu sehen.

Mir ist keine ideale Möglichkeit, dies mit Standard -SQL zu tun.

Es gibt noch einen anderen Weg; Wenn es vernünftig ist zu erwarten, dass die fraglichen Objekte bereits in die Sitzung geladen sind; Sie haben sie bereits in derselben Transaktion auf sie zugegriffen. Sie können stattdessen:

map(session.query(Record).get, seq)

In dem Fall, in dem diese Objekte bereits vorhanden sind, wird dies viel schneller sein, da es keine Abfragen gibt, um diese Objekte abzurufen. Andererseits, wenn mehr als eine winzige Anzahl dieser Objekte sind nicht geladen ist es viel, viel langsamer, da es eine Abfrage pro fehlender Instanz verursacht, anstelle einer einzelnen Abfrage für alle Objekte.

Dies kann nützlich sein, wenn Sie dies tun joinedload() Abfragen, bevor Sie den obigen Schritt erreichen, können Sie sicher sein, dass sie bereits geladen wurden. Im Allgemeinen sollten Sie die Lösung in der Frage standardmäßig verwenden und diese Lösung nur untersuchen, wenn Sie gesehen haben, dass Sie immer wieder dieselben Objekte abfragen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top