Ich habe eine Reihe von IDs, die ich abrufen möchte. Das ist ganz einfach:
session.query(Record).filter(Record.id.in_(seq)).all()
Gibt es einen besseren Weg, dies zu tun?
Ich habe eine Reihe von IDs, die ich abrufen möchte. Das ist ganz einfach:
session.query(Record).filter(Record.id.in_(seq)).all()
Gibt es einen besseren Weg, dies zu tun?
Der jetzige Code ist völlig in Ordnung. Allerdings ist jemand fragt mich für einige System der Absicherung zwischen den beiden Ansätzen zu tun, eine große IN vs. mit get() für einzelne IDs.
Wenn jemand wirklich versucht, den SELECT zu vermeiden, dann ist es am besten, die benötigten Objekte schon vorher im Speicher anzulegen. Zum Beispiel arbeiten Sie an einer großen Tabelle mit Elementen. Teilen Sie die Arbeit in Teile auf, z. B. ordnen Sie die gesamte Arbeit nach dem Primärschlüssel oder nach dem Datumsbereich, was auch immer, und laden Sie dann alles für diesen Teil 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 aber auch einige SQLAlchemy-APIs zu veranschaulichen, können wir eine Funktion erstellen, die das IN für Datensätze, die wir nicht haben, und ein lokales Get für diejenigen, die wir haben, ausführt. 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
Es gibt noch eine andere Möglichkeit: Wenn man davon ausgehen kann, dass die betreffenden Objekte bereits in die Sitzung geladen sind, weil man in derselben Transaktion bereits auf sie zugegriffen hat, kann man stattdessen Folgendes tun:
map(session.query(Record).get, seq)
Wenn diese Objekte bereits vorhanden sind, ist dies sehr viel schneller, da keine Abfragen zum Abrufen dieser Objekte erforderlich sind; wenn jedoch mehr als nur eine winzige Anzahl dieser Objekte vorhanden ist nicht geladen ist, wird sie viel, viel langsamer sein, da sie eine Abfrage pro fehlender Instanz auslöst, statt einer einzigen Abfrage für alle Objekte.
Dies kann nützlich sein, wenn Sie Folgendes tun joinedload()
Abfragen, bevor Sie zu dem obigen Schritt gelangen, damit Sie sicher sein können, dass sie bereits geladen wurden. Im Allgemeinen sollten Sie standardmäßig die Lösung in der Frage verwenden und diese Lösung erst dann untersuchen, wenn Sie festgestellt haben, dass Sie immer wieder dieselben Objekte abfragen.
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.
1 Stimmen
Was gefällt Ihnen daran nicht? Funktioniert es nicht? Es sieht so aus, als sollte es funktionieren.
0 Stimmen
Es funktioniert, ich habe mich nur gefragt, ob es einen schöneren Weg gibt, das zu tun.
1 Stimmen
Was meinen Sie mit "schöner"? Was gefällt Ihnen daran nicht?
1 Stimmen
Seien Sie vorsichtig, wenn
seq
lang genug wird, kann es zu einer Ausnahme "zu viele SQL-Variablen" kommen, weil die IN-Klausel parametrisiert ist und zu viele Parameter enthält.