Nein, es gibt keinen Unterschied im DB-Verkehr. Der Unterschied besteht nur darin, dass for row in session.Query(Model1)
das ORM in jeder Zeile funktioniert, wenn es Ihnen übergeben werden soll, während for row in session.Query(Model1).all()
das ORM in allen Zeilen funktioniert, bevor es Ihnen gegeben wird.
Beachten Sie, dass dies q.all()
nur Zucker ist list(q)
, dh alles, was der Generator liefert, in einer Liste zu sammeln. Hier ist der Quellcode dafür in der Query
Klasse (finden Sie def all
in der verknüpften Quelle):
def all(self):
"""Return the results represented by this ``Query`` as a list.
This results in an execution of the underlying query.
"""
return list(self)
... wobei self
das Abfrageobjekt iterierbar ist, dh eine __iter__
Methode hat.
Logischerweise sind die beiden Möglichkeiten in Bezug auf den DB-Verkehr genau gleich. Am Ende rufen beide query.__iter__()
an, um einen Zeileniterator zu erhalten, und arbeiten next()
sich durch diesen.
Der praktische Unterschied ist , dass der ehemalige kann beginnen Sie Zeilen , sobald ihre Daten angekommen geben „Streaming“ der DB Ergebnismenge zu Ihnen, mit weniger Speicherverbrauch und Latenz. Ich kann nicht sicher sagen, dass alle aktuellen Engine-Implementierungen dies tun (ich hoffe, dass sie es tun!). In jedem Fall verhindert die letztere Version diese Effizienz ohne guten Grund.
__iter__
tut alle Ergebnisse Prefetch (aus gutem Grund), aber dieses Verhalten kann geändert werden , wenn Sie wissen , was Sie tun.q.all()
?list(q)
, denke ich. Verhalten und Leistung sind jedoch gleich. Und das Wiederholenq.all()
ist zumindest potenziell langsamer zu starten und speicherhungriger als das Wiederholenq
.all
die gesamte Ergebnismenge in den Speicher geladen wird. Dies können Gigabyte an Daten sein.Tatsächlich ist die akzeptierte Antwort nicht wahr (oder zumindest nicht mehr, wenn überhaupt wahr), insbesondere sind die folgenden Aussagen falsch:
SQLAlchemy ordnet immer alle Zeilen ORM zu, unabhängig davon, welche der beiden Optionen Sie verwenden. Dies kann in ihrem Quellcode innerhalb dieser Zeilen gesehen werden ; Die
loading.instances
Methode gibt zwar einen Generator zurück, aber eine der bereits zugeordneten ORM-Instanzen. Sie können dies im eigentlichen Generatorschleifencode bestätigen :for row in rows: # ``rows`` here are already ORM mapped rows yield row
Bis der erste Lauf des Generators beendet ist und eine Instanz ausgegeben wurde, wurden alle Instanzen ORM-zugeordnet. (Nachstehend beispielhaft dargestellt (1))
Wie oben erläutert, ist dies auch falsch, da alle aus der Datenbank abgerufenen Daten verarbeitet / zugeordnet werden, bevor eine Nachgiebigkeit erfolgt.
Die Kommentare sind auch irreführend. Speziell:
Die gesamte Ergebnismenge wird unabhängig von der Verwendung geladen
.all()
oder nicht. In dieser Zeile deutlich zu sehen . Dies ist auch sehr einfach zu testen / zu verifizieren.Der einzige Unterschied, den ich aus den beiden angegebenen Optionen erkennen kann, besteht darin, dass Sie bei Verwendung
.all
zweimal die Ergebnisse durchlaufen (wenn Sie alle Instanzen / Zeilen verarbeiten möchten), da der Generator zuerst durch den Aufruf iteriert / erschöpft wird, in denlist(self)
er transformiert werden soll eine Liste.Da der SQLAlchemy-Code nicht leicht zu verdauen ist, habe ich einen kurzen Ausschnitt geschrieben, um all dies zu veranschaulichen:
class Query: def all(self): return list(self) def instances(self, rows_to_fetch=5): """ORM instance generator""" mapped_rows = [] for i in range(rows_to_fetch): # ORM mapping work here as in lines 81-88 from loading.instances mapped_rows.append(i) print("orm work finished for all rows") for row in mapped_rows: # same as ``yield from mapped_rows`` print("yield row") yield row def __iter__(self): return self.instances() query = Query() print("(1) Generator scenario:") print("First item of generator: ", next(iter(query))) print("\n(2) List scenario:") print("First item of list: ", query.all()[0]) """ RESULTS: -------- (1) Generator scenario: orm work finished for all rows yield row First item of generator: 0 (2) List scenario: orm work finished for all rows yield row yield row yield row yield row yield row First item of list: 0 """
Um eine feinere Kontrolle bei der Verarbeitung großer Ergebnismengen zu haben, müsste beispielsweise so etwas wie Yield_per verwendet werden, und dennoch wäre dies kein Einzelszenario , sondern die ORM-Zuordnung nach mehreren Instanzen.
Erwähnenswert ist der einzige Kommentar , der tatsächlich zutreffend war und leicht übersehen werden kann (daher das Schreiben dieser Antwort) und auf eine Erklärung von Michael Bayer hinweist .
quelle