Dieser Abschnitt beschreibt die direkte Verwendung von Engine, Connection und verwandten Objekten. Es ist wichtig zu beachten, dass bei der Verwendung des SQLAlchemy ORM diese Objekte im Allgemeinen nicht aufgerufen werden; stattdessen wird das Objekt Session als Schnittstelle zur Datenbank verwendet. Für Anwendungen, die auf der direkten Verwendung von Text-SQL-Anweisungen und/oder SQL-Ausdruckskonstrukten ohne die Beteiligung der höherstufigen Verwaltungsdienste des ORM basieren, sind jedoch die Engine und Connection Könige (und Königinnen?) – lesen Sie weiter.
Die typische Verwendung von create_engine() erfolgt einmal pro bestimmter Datenbank-URL, wird global für die Lebensdauer eines einzelnen Anwendungsprozesses gehalten. Eine einzelne Engine verwaltet viele einzelne DBAPI-Verbindungen im Auftrag des Prozesses und ist für die gleichzeitige Nutzung vorgesehen. Die Engine ist nicht gleichbedeutend mit der DBAPI-Funktion connect(), die nur eine Verbindungressource darstellt – die Engine ist am effizientesten, wenn sie nur einmal auf Modulebene einer Anwendung erstellt wird, nicht pro Objekt oder pro Funktionsaufruf.
Die grundlegendste Funktion der Engine ist die Bereitstellung des Zugriffs auf eine Connection, mit der SQL-Anweisungen aufgerufen werden können. Um eine Textanweisung an die Datenbank zu senden, sieht das so aus:
fromsqlalchemyimporttextwithengine.connect()asconnection:result=connection.execute(text("select username from users"))forrowinresult:print("username:",row.username)
Oben gibt die Methode Engine.connect() ein Connection-Objekt zurück, und durch die Verwendung in einem Python-Kontextmanager (z. B. der with:-Anweisung) wird die Methode Connection.close() am Ende des Blocks automatisch aufgerufen. Die Connection ist ein Proxy-Objekt für eine tatsächliche DBAPI-Verbindung. Die DBAPI-Verbindung wird aus dem Verbindungs-Pool zum Zeitpunkt der Erstellung der Connection abgerufen.
Das zurückgegebene Objekt ist als CursorResult bekannt, das auf einen DBAPI-Cursor verweist und Methoden zum Abrufen von Zeilen bereitstellt, ähnlich wie der DBAPI-Cursor. Der DBAPI-Cursor wird vom CursorResult geschlossen, wenn alle seine Ergebniszeilen (falls vorhanden) erschöpft sind. Ein CursorResult, das keine Zeilen zurückgibt, wie z. B. eine UPDATE-Anweisung (ohne zurückgegebene Zeilen), gibt die Cursor-Ressourcen sofort nach der Erstellung frei.
Wenn die Connection am Ende des with:-Blocks geschlossen wird, wird die referenzierte DBAPI-Verbindung an den Verbindungs-Pool freigegeben. Aus der Sicht der Datenbank selbst wird der Verbindungs-Pool die Verbindung nicht tatsächlich „schließen“, vorausgesetzt, der Pool hat Platz, um diese Verbindung für die nächste Verwendung zu speichern. Wenn die Verbindung zur Wiederverwendung an den Pool zurückgegeben wird, gibt der Pooling-Mechanismus einen rollback()-Aufruf auf der DBAPI-Verbindung aus, damit jeder transaktionale Zustand oder Sperren entfernt werden (dies wird als Reset On Return bezeichnet) und die Verbindung für ihre nächste Verwendung bereit ist.
Unser obiges Beispiel illustrierte die Ausführung eines Text-SQL-Strings, der durch die Verwendung der text()-Konstruktion aufgerufen werden sollte, um anzuzeigen, dass wir Text-SQL verwenden möchten. Die Methode Connection.execute() kann natürlich mehr als das aufnehmen; siehe Arbeiten mit Daten im SQLAlchemy Unified Tutorial für ein Tutorial.
Dieser Abschnitt beschreibt die Verwendung von Transaktionen bei der direkten Arbeit mit Engine- und Connection-Objekten. Bei der Verwendung des SQLAlchemy ORM ist die öffentliche API zur Transaktionskontrolle über das Objekt Session, das intern das Objekt Transaction verwendet. Weitere Informationen finden Sie unter Verwalten von Transaktionen.
Das Objekt Connection gibt SQL-Anweisungen immer im Kontext eines Transaktionsblocks aus. Beim ersten Aufruf der Methode Connection.execute() zur Ausführung einer SQL-Anweisung wird diese Transaktion automatisch gestartet, was als autobegin bezeichnet wird. Die Transaktion bleibt für den Geltungsbereich des Objekts Connection bestehen, bis die Methoden Connection.commit() oder Connection.rollback() aufgerufen werden. Nach dem Ende der Transaktion wartet die Connection darauf, dass die Methode Connection.execute() erneut aufgerufen wird, woraufhin sie wieder automatisch beginnt.
Dieser Aufrufstil wird als commit as you go bezeichnet und im folgenden Beispiel illustriert
withengine.connect()asconnection:connection.execute(some_table.insert(),{"x":7,"y":"this is some data"})connection.execute(some_other_table.insert(),{"q":8,"p":"this is some more data"})connection.commit()# commit the transaction
Im „commit as you go“-Stil können wir die Methoden Connection.commit() und Connection.rollback() innerhalb einer laufenden Sequenz anderer Anweisungen, die mit Connection.execute() ausgegeben werden, frei aufrufen; jedes Mal wird die Transaktion beendet, und wenn eine neue Anweisung ausgegeben wird, beginnt implizit eine neue Transaktion.
withengine.connect()asconnection:connection.execute(text("<some statement>"))connection.commit()# commits "some statement"# new transaction startsconnection.execute(text("<some other statement>"))connection.rollback()# rolls back "some other statement"# new transaction startsconnection.execute(text("<a third statement>"))connection.commit()# commits "a third statement"
Neu in Version 2.0: Der „commit as you go“-Stil ist eine neue Funktion von SQLAlchemy 2.0. Er ist auch in SQLAlchemy 1.4 im „transitional“-Modus verfügbar, wenn eine „future“-Engine verwendet wird.
Das Objekt Connection bietet einen expliziteren Transaktionsverwaltungsstil, der als begin once bekannt ist. Im Gegensatz zu „commit as you go“ erlaubt „begin once“ den Startpunkt der Transaktion explizit anzugeben, und erlaubt, dass die Transaktion selbst als Kontextmanager-Block ausgelegt wird, so dass das Ende der Transaktion implizit erfolgt. Um „begin once“ zu verwenden, wird die Methode Connection.begin() verwendet, die ein Objekt Transaction zurückgibt, das die DBAPI-Transaktion repräsentiert. Dieses Objekt unterstützt auch die explizite Verwaltung über seine eigenen Methoden Transaction.commit() und Transaction.rollback(), unterstützt aber als bevorzugte Praxis auch die Kontextmanager-Schnittstelle, bei der sie sich selbst committet, wenn der Block normal endet und ein Rollback ausgibt, wenn eine Ausnahme ausgelöst wird, bevor die Ausnahme nach außen weitergegeben wird. Unten wird die Form eines „begin once“-Blocks illustriert.
withengine.connect()asconnection:withconnection.begin():connection.execute(some_table.insert(),{"x":7,"y":"this is some data"})connection.execute(some_other_table.insert(),{"q":8,"p":"this is some more data"})# transaction is committed
Eine praktische Abkürzungsform für den obigen „begin once“-Block ist die Verwendung der Methode Engine.begin() auf der Ebene des ursprünglichen Objekts Engine, anstatt die zwei separaten Schritte Engine.connect() und Connection.begin() auszuführen; die Methode Engine.begin() gibt einen speziellen Kontextmanager zurück, der intern sowohl den Kontextmanager für die Connection als auch den Kontextmanager für die Transaction verwaltet, die normalerweise von der Methode Connection.begin() zurückgegeben wird.
withengine.begin()asconnection:connection.execute(some_table.insert(),{"x":7,"y":"this is some data"})connection.execute(some_other_table.insert(),{"q":8,"p":"this is some more data"})# transaction is committed, and Connection is released to the connection# pool
Tipp
Innerhalb des Blocks Engine.begin() können wir die Methoden Connection.commit() oder Connection.rollback() aufrufen, die die Transaktion normal beenden, die durch den Block vorzeitig abgegrenzt wurde. Wenn wir dies jedoch tun, können keine weiteren SQL-Operationen auf der Connection ausgeführt werden, bis der Block endet.
>>> fromsqlalchemyimportcreate_engine>>> e=create_engine("sqlite://",echo=True)>>> withe.begin()asconn:... conn.commit()... conn.begin()2021-11-08 09:49:07,517 INFO sqlalchemy.engine.Engine BEGIN (implicit)2021-11-08 09:49:07,517 INFO sqlalchemy.engine.Engine COMMITTraceback (most recent call last):...sqlalchemy.exc.InvalidRequestError: Can't operate on closed transaction insidecontext manager. Please complete the context manager before emittingfurther commands.
Die Stile „commit as you go“ und „begin once“ können innerhalb eines einzigen Blocks Engine.connect() frei gemischt werden, vorausgesetzt, der Aufruf von Connection.begin() steht nicht im Konflikt mit dem „autobegin“-Verhalten. Um dies zu erreichen, sollte Connection.begin() entweder vor der Ausgabe von SQL-Anweisungen aufgerufen werden oder direkt nach einem vorherigen Aufruf von Connection.commit() oder Connection.rollback().
withengine.connect()asconnection:withconnection.begin():# run statements in a "begin once" blockconnection.execute(some_table.insert(),{"x":7,"y":"this is some data"})# transaction is committed# run a new statement outside of a block. The connection# autobeginsconnection.execute(some_other_table.insert(),{"q":8,"p":"this is some more data"})# commit explicitlyconnection.commit()# can use a "begin once" block herewithconnection.begin():# run more statementsconnection.execute(...)
Wenn Code entwickelt wird, der „begin once“ verwendet, löst die Bibliothek InvalidRequestError aus, wenn eine Transaktion bereits „autobegonnen“ wurde.
Festlegen von Transaktions-Isolationsgraden einschließlich DBAPI Autocommit¶
Die meisten DBAPIs unterstützen das Konzept konfigurierbarer Transaktions- Isolationsgrade. Dies sind traditionell die vier Grade „READ UNCOMMITTED“, „READ COMMITTED“, „REPEATABLE READ“ und „SERIALIZABLE“. Diese werden normalerweise auf eine DBAPI-Verbindung angewendet, bevor sie eine neue Transaktion beginnt, wobei zu beachten ist, dass die meisten DBAPIs diese Transaktion implizit starten, wenn SQL-Anweisungen zum ersten Mal ausgegeben werden.
DBAPIs, die Isolationsgrade unterstützen, unterstützen auch normalerweise das Konzept des echten „Autocommit“, was bedeutet, dass die DBAPI-Verbindung selbst in einen nicht-transaktionalen Autocommit-Modus versetzt wird. Dies bedeutet normalerweise, dass das typische DBAPI-Verhalten, automatisch „BEGIN“ an die Datenbank zu senden, nicht mehr stattfindet, es kann aber auch andere Direktiven beinhalten. SQLAlchemy behandelt das Konzept von „Autocommit“ wie jeden anderen Isolationsgrad; es ist ein Isolationsgrad, der nicht nur „read committed“, sondern auch die Atomarität verliert.
Tipp
Es ist wichtig zu beachten, wie im Folgenden im Abschnitt Verständnis des DBAPI-Level Autocommit Isolationsgrads näher erläutert wird, dass der Isolationsgrad „autocommit“ wie jeder andere Isolationsgrad das „transaktionale“ Verhalten der Connection nicht beeinflusst; diese ruft weiterhin die DBAPI-Methoden .commit() und .rollback() auf (sie haben einfach keine Wirkung im Autocommit-Modus), und für die Methode .begin() wird angenommen, dass die DBAPI implizit eine Transaktion startet (was bedeutet, dass SQLAlchemys „begin“ den Autocommit-Modus nicht ändert).
SQLAlchemy-Dialekte sollten diese Isolationsgrade sowie Autocommit so weit wie möglich unterstützen.
Festlegen des Isolationsgrads oder des DBAPI Autocommit für eine Verbindung¶
# possible values for Connection.execution_options(isolation_level="<value>")"AUTOCOMMIT""READ COMMITTED""READ UNCOMMITTED""REPEATABLE READ""SERIALIZABLE"
Nicht jede DBAPI unterstützt jeden Wert; wenn ein nicht unterstützter Wert für ein bestimmtes Backend verwendet wird, wird ein Fehler ausgelöst.
Um beispielsweise REPEATABLE READ für eine bestimmte Verbindung zu erzwingen und dann eine Transaktion zu beginnen.
Der Rückgabewert der Methode Connection.execution_options() ist dasselbe Objekt Connection, auf dem die Methode aufgerufen wurde, d.h. sie modifiziert den Zustand des Objekts Connection vor Ort. Dies ist ein neues Verhalten ab SQLAlchemy 2.0. Dieses Verhalten gilt nicht für die Methode Engine.execution_options(); diese Methode gibt weiterhin eine Kopie der Engine zurück und kann, wie unten beschrieben, verwendet werden, um mehrere Objekte Engine mit unterschiedlichen Ausführungsoptionen zu erstellen, die jedoch denselben Dialekt und denselben Verbindungs-Pool teilen.
Hinweis
Der Parameter Connection.execution_options.isolation_level gilt notwendigerweise nicht für Optionen auf Anweisungsebene, wie z. B. die von Executable.execution_options(), und wird auf dieser Ebene abgelehnt. Dies liegt daran, dass die Option auf einer DBAPI-Verbindung auf Transaktionsbasis gesetzt werden muss.
Festlegen des Isolationsgrads oder des DBAPI Autocommit für eine Engine¶
Mit der obigen Einstellung wird jede neue DBAPI-Verbindung im Moment ihrer Erstellung so konfiguriert, dass sie eine "REPEATABLEREAD"-Isolationsstufe für alle nachfolgenden Operationen verwendet.
Verwaltung mehrerer Isolationsstufen für eine einzelne Engine¶
Die Isolationsstufe kann auch pro Engine mit einem potenziell größeren Grad an Flexibilität gesetzt werden, entweder über den Parameter create_engine.execution_options an create_engine() oder über die Methode Engine.execution_options(), wobei letztere eine Kopie der Engine erstellt, die das Dialekt und den Connection-Pool der ursprünglichen Engine teilt, aber ihre eigene Isolationsstufeneinstellung pro Verbindung hat.
Mit der obigen Einstellung wird die DBAPI-Verbindung so konfiguriert, dass sie eine "REPEATABLEREAD"-Isolationsstufe für jede neu begonnene Transaktion verwendet; die Verbindung wird jedoch beim Pooling auf die ursprüngliche Isolationsstufe zurückgesetzt, die vorhanden war, als die Verbindung zuerst auftrat. Auf der Ebene von create_engine() ist der Endeffekt nicht anders, als den Parameter create_engine.isolation_level zu verwenden.
Eine Anwendung, die häufig Operationen innerhalb verschiedener Isolationsstufen ausführt, möchte möglicherweise mehrere „Sub-Engines“ einer Haupt-Engine erstellen, von denen jede auf eine andere Isolationsstufe konfiguriert ist. Ein solcher Anwendungsfall ist eine Anwendung, die Operationen hat, die in „transaktionale“ und „nur-lesende“ Operationen unterteilt sind; eine separate Engine, die "AUTOCOMMIT" verwendet, kann von der Haupt-Engine abgetrennt werden.
Oben erstellt die Methode Engine.execution_options() eine flache Kopie der ursprünglichen Engine. Sowohl eng als auch autocommit_engine teilen sich dasselbe Dialekt und denselben Connection-Pool. Der „AUTOCOMMIT“-Modus wird jedoch für Verbindungen gesetzt, wenn sie von autocommit_engine abgerufen werden.
Die Einstellung der Isolationsstufe, welche auch immer es ist, wird bedingungslos zurückgesetzt, wenn eine Verbindung zurück in den Connection-Pool gelegt wird.
Verständnis der DBAPI-Level Autocommit-Isolationsstufe¶
Im übergeordneten Abschnitt haben wir das Konzept des Parameters Connection.execution_options.isolation_level und wie er verwendet werden kann, um Datenbank-Isolationsstufen zu setzen, einschließlich DBAPI-Level „autocommit“, das von SQLAlchemy als eine weitere Transaktionsisolationsstufe behandelt wird, eingeführt. In diesem Abschnitt werden wir versuchen, die Implikationen dieses Ansatzes zu klären.
Wenn wir ein Connection-Objekt auschecken und es im „autocommit“-Modus verwenden möchten, würden wir wie folgt vorgehen:
Oben ist die normale Verwendung des „DBAPI Autocommit“-Modus dargestellt. Es ist nicht notwendig, Methoden wie Connection.begin() oder Connection.commit() zu verwenden, da alle Anweisungen sofort an die Datenbank übermittelt werden. Wenn der Block endet, wird das Connection-Objekt die „autocommit“-Isolationsstufe zurücksetzen, und die DBAPI-Verbindung wird an den Connection-Pool zurückgegeben, wo die DBAPI-Methode connection.rollback() normalerweise aufgerufen wird, aber da die obigen Anweisungen bereits committet wurden, hat dieses Rollback keine Auswirkung auf den Zustand der Datenbank.
Es ist wichtig zu beachten, dass der „autocommit“-Modus auch dann bestehen bleibt, wenn die Methode Connection.begin() aufgerufen wird; die DBAPI wird keine BEGIN an die Datenbank senden, noch wird sie COMMIT senden, wenn Connection.commit() aufgerufen wird. Diese Verwendung ist auch kein Fehlerfall, da erwartet wird, dass die „autocommit“-Isolationsstufe auf Code angewendet werden kann, der ansonsten unter Annahme eines transaktionalen Kontexts geschrieben wurde; die „Isolationsstufe“ ist schließlich ein Konfigurationsdetail der Transaktion selbst, wie jede andere Isolationsstufe auch.
Im folgenden Beispiel bleiben die Anweisungen autocommitten, unabhängig von den Transaktionsblöcken auf SQLAlchemy-Ebene.
withengine.connect()asconnection:connection=connection.execution_options(isolation_level="AUTOCOMMIT")# this begin() does not affect the DBAPI connection, isolation stays at AUTOCOMMITwithconnection.begin()astrans:connection.execute(text("<statement>"))connection.execute(text("<statement>"))
Wenn wir einen Block wie den obigen mit aktiviertem Logging ausführen, versucht das Logging anzuzeigen, dass, obwohl ein DBAPI-Level .commit() aufgerufen wird, dieser aufgrund des Autocommit-Modus wahrscheinlich keine Wirkung hat.
INFO sqlalchemy.engine.Engine BEGIN (implicit)
...
INFO sqlalchemy.engine.Engine COMMIT using DBAPI connection.commit(), DBAPI should ignore due to autocommit mode
Gleichzeitig bleiben, auch wenn wir „DBAPI Autocommit“ verwenden, die transaktionalen Semantiken von SQLAlchemy, d. h. das In-Python-Verhalten von Connection.begin() sowie das Verhalten von „autobegin“, **bestehen, auch wenn diese die DBAPI-Verbindung selbst nicht beeinflussen**. Zur Veranschaulichung wird der folgende Code einen Fehler auslösen, da Connection.begin() nach dem bereits erfolgten Autobegin aufgerufen wird.
withengine.connect()asconnection:connection=connection.execution_options(isolation_level="AUTOCOMMIT")# "transaction" is autobegin (but has no effect due to autocommit)connection.execute(text("<statement>"))# this will raise; "transaction" is already begunwithconnection.begin()astrans:connection.execute(text("<statement>"))
Das obige Beispiel demonstriert ebenfalls dasselbe Thema, dass die „autocommit“-Isolationsstufe ein Konfigurationsdetail der zugrunde liegenden Datenbanktransaktion ist und unabhängig vom Begin/Commit-Verhalten des SQLAlchemy Connection-Objekts ist. Der „autocommit“-Modus interagiert in keiner Weise mit Connection.begin(), und die Connection konsultiert diesen Status nicht, wenn sie ihre eigenen Zustandsänderungen in Bezug auf die Transaktion durchführt (mit Ausnahme der Andeutung im Engine-Logging, dass diese Blöcke nicht tatsächlich committen). Der Grund für dieses Design ist, ein vollständig konsistentes Nutzungsmuster mit der Connection beizubehalten, bei dem der DBAPI-Autocommit-Modus unabhängig geändert werden kann, ohne dass dies zu Codeänderungen an anderer Stelle führt.
Isolationsstufeneinstellungen, einschließlich des Autocommit-Modus, werden automatisch zurückgesetzt, wenn die Verbindung zurück in den Connection-Pool gelegt wird. Daher ist es vorzuziehen, den Wechsel von Isolationsstufen bei einem einzelnen Connection-Objekt zu vermeiden, da dies zu übermäßiger Ausführlichkeit führt.
Um zu veranschaulichen, wie „autocommit“ in einem Ad-hoc-Modus im Geltungsbereich einer einzelnen Connection-Ausleihe verwendet wird, muss der Parameter Connection.execution_options.isolation_level mit der vorherigen Isolationsstufe neu angewendet werden. Der vorherige Abschnitt zeigte den Versuch, Connection.begin() aufzurufen, um eine Transaktion zu starten, während Autocommit stattfand; wir können dieses Beispiel umschreiben, um dies tatsächlich zu tun, indem wir zuerst die Isolationsstufe zurücksetzen, bevor wir Connection.begin() aufrufen.
# if we wanted to flip autocommit on and off on a single connection/# which... we usually don't.withengine.connect()asconnection:connection.execution_options(isolation_level="AUTOCOMMIT")# run statement(s) in autocommit modeconnection.execute(text("<statement>"))# "commit" the autobegun "transaction"connection.commit()# switch to default isolation levelconnection.execution_options(isolation_level=connection.default_isolation_level)# use a begin blockwithconnection.begin()astrans:connection.execute(text("<statement>"))
Oben haben wir Connection.default_isolation_level verwendet, um die Standard-Isolationsstufe wiederherzustellen (vorausgesetzt, das ist es, was wir hier wollen). Es ist jedoch wahrscheinlich besser, mit der Architektur der Connection zu arbeiten, die das Zurücksetzen der Isolationsstufe beim Check-in automatisch handhabt. Der **bevorzugte** Weg, das Obige zu schreiben, ist die Verwendung von zwei Blöcken.
# use an autocommit blockwithengine.connect().execution_options(isolation_level="AUTOCOMMIT")asconnection:# run statement in autocommit modeconnection.execute(text("<statement>"))# use a regular blockwithengine.begin()asconnection:connection.execute(text("<statement>"))
Zusammenfassend
Die „DBAPI-Level Autocommit“-Isolationsstufe ist vollständig unabhängig von der Vorstellung des Connection-Objekts von „begin“ und „commit“.
Verwenden Sie einzelne Connection-Checkouts pro Isolationsstufe. Vermeiden Sie es, zwischen „autocommit“ bei einem einzelnen Verbindungs-Checkout hin und her zu wechseln; lassen Sie die Engine die Arbeit machen, die Standard-Isolationsstufen wiederherzustellen.
Serverseitige Cursor verwenden (a. k. a. Ergebnisse streamen)¶
Einige Backends unterstützen explizit das Konzept von „serverseitigen Cursorn“ im Gegensatz zu „clientseitigen Cursorn“. Ein clientseitiger Cursor bedeutet hier, dass der Datenbanktreiber alle Zeilen eines Ergebnisdatensatzes vollständig in den Speicher lädt, bevor er von einer Anweisungsausführung zurückkehrt. Treiber wie die von PostgreSQL und MySQL/MariaDB verwenden standardmäßig clientseitige Cursor. Ein serverseitiger Cursor hingegen bedeutet, dass Ergebniszeilen im Zustand des Datenbankservers ausstehend bleiben, während Ergebniszeilen vom Client verbraucht werden. Die Treiber für Oracle Database verwenden beispielsweise im Allgemeinen ein „serverseitiges“ Modell, und das SQLite-Dialekt verwendet zwar keine echte „Client/Server“-Architektur, verwendet aber dennoch einen ungepufferten Ansatz zum Abrufen von Ergebnissen, der Ergebniszeilen außerhalb des Prozessspeichers belässt, bevor sie verbraucht werden.
Aus dieser grundlegenden Architektur folgt, dass ein „serverseitiger Cursor“ speichereffizienter beim Abrufen sehr großer Ergebnisdatensätze ist, während er gleichzeitig mehr Komplexität im Client-Server-Kommunikationsprozess einführen und für kleine Ergebnisdatensätze (typischerweise weniger als 10000 Zeilen) weniger effizient sein kann.
Für diejenigen Dialekte, die bedingte Unterstützung für gepufferte oder ungepufferte Ergebnisse bieten, gibt es normalerweise Vorbehalte bei der Verwendung des „ungepufferten“ oder serverseitigen Cursormodus. Bei Verwendung des psycopg2-Dialekts beispielsweise wird ein Fehler ausgelöst, wenn ein serverseitiger Cursor mit einer DML- oder DDL-Anweisung verwendet wird. Bei Verwendung von MySQL-Treibern mit einem serverseitigen Cursor befindet sich die DBAPI-Verbindung in einem fragileren Zustand und erholt sich nicht so gut von Fehlerbedingungen, noch erlaubt sie ein Rollback, bis der Cursor vollständig geschlossen ist.
Aus diesem Grund werden die Dialekte von SQLAlchemy immer die weniger fehleranfällige Version eines Cursors standardmäßig verwenden, was bedeutet, dass für PostgreSQL- und MySQL-Dialekte standardmäßig ein gepufferter „clientseitiger“ Cursor verwendet wird, bei dem der vollständige Ergebnissatz in den Speicher geladen wird, bevor irgendwelche Fetch-Methoden vom Cursor aufgerufen werden. Diese Betriebsart ist in den **allermeisten** Fällen geeignet; ungepufferte Cursor sind im Allgemeinen nur in dem seltenen Fall einer Anwendung nützlich, die eine sehr große Anzahl von Zeilen in Chunks abruft, wobei die Verarbeitung dieser Zeilen abgeschlossen sein kann, bevor weitere Zeilen abgerufen werden.
Da einzelne Zeilenabrufoperationen mit vollständig ungepufferten serverseitigen Cursorn in der Regel teurer sind als das gleichzeitige Abrufen von Zeilenstapeln, konfiguriert die Ausführungsoption Connection.execution_options.yield_per eine Connection oder Anweisung zur Verwendung serverseitiger Cursor, sofern verfügbar und nicht bereits Standardverhalten für diesen Backend. Gleichzeitig wird ein fester Zeilenpuffer konfiguriert, der Zeilen vom Server in Stapeln abruft, während sie verbraucht werden. Dieser Parameter kann über die Methode Connection.execution_options() auf der Connection oder auf einer Anweisung über die Methode Executable.execution_options() an einen positiven Ganzzahlwert übergeben werden.
Die Verwendung dieser Option ist gleichbedeutend mit dem manuellen Setzen der Option Connection.execution_options.stream_results, die im nächsten Abschnitt beschrieben wird, und anschließender Aufruf der Methode Result.yield_per() auf dem Result-Objekt mit dem angegebenen Ganzzahlwert. In beiden Fällen beinhaltet die Wirkung dieser Kombination:
Der serverseitige Cursormodus wird für das gegebene Backend ausgewählt, sofern verfügbar und nicht bereits das Standardverhalten für dieses Backend.
Beim Abrufen von Ergebniszeilen werden diese in Stapeln gepuffert, wobei die Größe jedes Stapels bis zum letzten Stapel gleich dem Ganzzahlargument ist, das an die Option Connection.execution_options.yield_per oder die Methode Result.yield_per() übergeben wurde; der letzte Stapel wird dann gegen die verbleibenden Zeilen kleiner als diese Größe bemessen.
Die Standardpartitionsgröße, die von der Methode Result.partitions() verwendet wird, falls diese verwendet wird, wird ebenfalls gleich dieser Ganzzahlgröße gemacht.
Diese drei Verhaltensweisen werden im folgenden Beispiel veranschaulicht.
withengine.connect()asconn:withconn.execution_options(yield_per=100).execute(text("select * from table"))asresult:forpartitioninresult.partitions():# partition is an iterable that will be at most 100 itemsforrowinpartition:print(f"{row}")
Das obige Beispiel veranschaulicht die Kombination von yield_per=100 und die Verwendung der Methode Result.partitions(), um die Verarbeitung von Zeilen in Stapeln durchzuführen, die der vom Server abgerufenen Größe entsprechen. Die Verwendung von Result.partitions() ist optional, und wenn das Result direkt iteriert wird, wird für jeweils 100 abgerufene Zeilen ein neuer Stapel von Zeilen gepuffert. Das Aufrufen einer Methode wie Result.all() sollte **nicht** verwendet werden, da dies alle verbleibenden Zeilen auf einmal abruft und den Zweck der Verwendung von yield_per zunichtemacht.
Tipp
Das Result-Objekt kann als Kontextmanager verwendet werden, wie oben gezeigt. Bei der Iteration mit einem serverseitigen Cursor ist dies der beste Weg, um sicherzustellen, dass das Result-Objekt geschlossen wird, auch wenn während des Iterationsprozesses Ausnahmen auftreten.
Neu in Version 1.4.40: Connection.execution_options.yield_per wurde als Ausführungsoption auf Core-Ebene hinzugefügt, um bequem Streaming-Ergebnisse, Puffergröße und Partitionsgröße gleichzeitig festzulegen, auf eine Weise, die auf den ähnlichen Anwendungsfall des ORM übertragbar ist.
Streaming mit einem dynamisch wachsenden Puffer unter Verwendung von stream_results¶
Wenn ein Result-Objekt, das mit der Option Connection.execution_options.stream_results geliefert wird, direkt iteriert wird, werden die Zeilen intern mit einem Standard-Pufferungsschema abgerufen, das zunächst eine kleine Menge von Zeilen, dann einen größeren und größeren Puffer bei jedem Abruf bis zu einem voreingestellten Limit von 1000 Zeilen puffert. Die maximale Größe dieses Puffers kann mit der Ausführungsoption Connection.execution_options.max_row_buffer beeinflusst werden.
withengine.connect()asconn:withconn.execution_options(stream_results=True,max_row_buffer=100).execute(text("select * from table"))asresult:forrowinresult:print(f"{row}")
Um Multi-Tenant-Anwendungen zu unterstützen, die gemeinsame Sätze von Tabellen auf mehrere Schemata verteilen, kann die Ausführungsoption Connection.execution_options.schema_translate_map verwendet werden, um einen Satz von Table-Objekten so umzufunktionieren, dass sie unter verschiedenen Schemanamen gerendert werden, ohne Änderungen.
Das heißt, der Schema-Name wird durch unseren übersetzten Namen ersetzt. Die Zuordnung kann beliebig viele Ziel->Ziel-Schemata angeben
connection=engine.connect().execution_options(schema_translate_map={None:"user_schema_one",# no schema name -> "user_schema_one""special":"special_schema",# schema="special" becomes "special_schema""public":None,# Table objects with schema="public" will render with no schema})
Der Parameter Connection.execution_options.schema_translate_map wirkt sich auf alle DDL- und SQL-Konstrukte aus, die aus der SQL-Ausdruckssprache generiert werden, wie sie von den Table- oder Sequence-Objekten abgeleitet werden. Er hat **keine** Auswirkung auf literalen SQL-String, der über das text()-Konstrukt oder über einfache Strings, die an Connection.execute() übergeben werden, verwendet wird.
Das Feature tritt **nur** in Fällen in Kraft, in denen der Name des Schemas direkt vom Namen einer Table oder Sequence abgeleitet wird; es hat keine Auswirkung auf Methoden, bei denen ein Schema-Name als String direkt übergeben wird. Nach diesem Muster tritt es bei den „can create“ / „can drop“-Prüfungen in Kraft, die von Methoden wie MetaData.create_all() oder MetaData.drop_all() aufgerufen werden, und es tritt in Kraft, wenn Tabellen-Reflektion mit einem Table-Objekt verwendet wird. Es hat jedoch **keine** Auswirkung auf die Operationen des Inspector-Objekts, da der Schema-Name diesen Methoden explizit übergeben wird.
Tipp
Um das Schema-Übersetzungs-Feature mit dem ORM Session zu verwenden, stellen Sie diese Option auf der Ebene der Engine ein und übergeben Sie diese Engine dann an die Session. Die Session verwendet für jede Transaktion eine neue Connection
Bei Verwendung der ORM Session ohne Erweiterungen wird das Schema-Übersetzungs-Feature nur als **eine einzige Schema-Übersetzungs-Zuordnung pro Session** unterstützt. Es funktioniert **nicht**, wenn unterschiedliche Schema-Übersetzungs-Zuordnungen pro Anweisung angegeben werden, da die ORM Session die aktuellen Schema-Übersetzungs-Werte nicht für einzelne Objekte berücksichtigt.
Um eine einzelne Session mit mehreren schema_translate_map-Konfigurationen zu verwenden, kann die Horizontal Sharding-Erweiterung verwendet werden. Siehe das Beispiel unter Horizontal Sharding.
SQLAlchemy verfügt über ein umfassendes Caching-System für den SQL-Compiler sowie seine ORM-Varianten. Dieses Caching-System ist transparent innerhalb der Engine und stellt sicher, dass der SQL-Kompilierungsprozess für eine gegebene Core- oder ORM-SQL-Anweisung sowie damit verbundene Berechnungen, die Ergebnis-Abrufmechanismen für diese Anweisung zusammenstellen, nur einmal für dieses Anweisungsobjekt und alle anderen mit identischer Struktur durchgeführt wird, solange die betreffende Struktur im „kompilierten Cache“ der Engine verbleibt. Mit „Anweisungsobjekte mit identischer Struktur“ ist im Allgemeinen eine SQL-Anweisung gemeint, die innerhalb einer Funktion konstruiert und jedes Mal, wenn diese Funktion ausgeführt wird, neu erstellt wird.
Die obige Anweisung generiert SQL, das SELECTid,colFROMtableWHEREcol=:colORDERBYid ähnelt. Beachten Sie, dass obwohl der Wert von parameter ein einfacher Python-Objekt wie ein String oder eine Ganzzahl ist, die String-SQL-Form der Anweisung diesen Wert nicht enthält, da sie gebundene Parameter verwendet. Nachfolgende Aufrufe der obigen Funktion run_my_statement() verwenden ein zwischengespeichertes Kompilierungskonstrukt im Geltungsbereich des connection.execute()-Aufrufs zur Leistungssteigerung.
Hinweis
Es ist wichtig zu beachten, dass der SQL-Kompilierungs-Cache nur den **an die Datenbank übergebenen SQL-String speichert** und **nicht die von einer Abfrage zurückgegebenen Daten**. Es handelt sich in keiner Weise um einen Daten-Cache und hat keine Auswirkungen auf die Ergebnisse einer bestimmten SQL-Anweisung und impliziert auch keinen Speicherverbrauch, der mit dem Abrufen von Ergebniszeilen verbunden ist.
Während SQLAlchemy seit der frühen 1.x-Serie einen rudimentären Anweisungs-Cache hatte und zusätzlich die „Baked Query“-Erweiterung für das ORM bot, erforderten beide Systeme eine hohe Dosis spezieller API-Nutzung, damit der Cache effektiv war. Der neue Cache ab 1.4 ist stattdessen vollständig automatisch und erfordert keine Änderung des Programmierstils, um effektiv zu sein.
Der Cache wird automatisch ohne Konfigurationsänderungen verwendet, und es sind keine speziellen Schritte erforderlich, um ihn zu aktivieren. Die folgenden Abschnitte beschreiben die Konfiguration und fortgeschrittenen Nutzungsmuster für den Cache.
Der Cache selbst ist ein Dictionary-ähnliches Objekt namens LRUCache, eine interne SQLAlchemy-Dictionary-Unterklasse, die die Nutzung bestimmter Schlüssel verfolgt und über einen periodischen „Bereinigungsschritt“ verfügt, der die am wenigsten zuletzt verwendeten Elemente entfernt, wenn die Größe des Caches einen bestimmten Schwellenwert erreicht. Die Größe dieses Caches beträgt standardmäßig 500 und kann über den Parameter create_engine.query_cache_size konfiguriert werden
Die Größe des Caches kann bis zu 150 % der angegebenen Größe anwachsen, bevor er auf die Zielgröße zurückgeführt wird. Ein Cache der Größe 1200 kann somit auf 1800 Elemente anwachsen, woraufhin er auf 1200 reduziert wird.
Die Dimensionierung des Caches basiert auf einem einzigen Eintrag pro eindeutiger SQL-Anweisung, die pro Engine gerendert wird. SQL-Anweisungen, die sowohl aus Core als auch aus ORM generiert werden, werden gleich behandelt. DDL-Anweisungen werden in der Regel nicht zwischengespeichert. Um festzustellen, was der Cache tut, enthält das Engine-Logging Details über das Verhalten des Caches, die im nächsten Abschnitt beschrieben werden.
Die obige Cache-Größe von 1200 ist eigentlich ziemlich groß. Für kleine Anwendungen ist eine Größe von 100 wahrscheinlich ausreichend. Um die optimale Größe des Caches abzuschätzen, unter der Annahme, dass auf dem Zielhost genügend Speicher vorhanden ist, sollte die Größe des Caches auf der Anzahl der eindeutigen SQL-Strings basieren, die für die verwendete Ziel-Engine gerendert werden können. Der schnellste Weg, dies zu sehen, ist die Verwendung der SQL-Echo-Funktion, die am direktesten durch die Option create_engine.echo oder durch Verwendung von Python-Logging aktiviert wird; siehe den Abschnitt Konfiguration von Logging für Hintergrundinformationen zur Logging-Konfiguration.
Als Beispiel untersuchen wir das Logging des folgenden Programms
Wenn es ausgeführt wird, enthält jede protokollierte SQL-Anweisung ein in Klammern gesetztes Cache-Statistik-Badge links von den übergebenen Parametern. Die vier Arten von Nachrichten, die wir sehen können, sind wie folgt zusammengefasst:
[rawsql] - der Treiber oder der Endbenutzer hat rohes SQL über Connection.exec_driver_sql() ausgegeben - Caching ist nicht anwendbar
[nokey] - das Anweisungsobjekt ist eine DDL-Anweisung, die nicht zwischengespeichert wird, oder das Anweisungsobjekt enthält nicht zwischenspeicherbare Elemente wie benutzerdefinierte Konstrukte oder beliebig große VALUES-Klauseln.
[generatedinXs] - die Anweisung war ein **Cache-Miss** und musste kompiliert und dann im Cache gespeichert werden. Es hat X Sekunden gedauert, das kompilierte Konstrukt zu erzeugen. Die Zahl X wird in Bruchteilen von Sekunden angegeben.
[cachedsinceXsago] - die Anweisung war ein **Cache-Hit** und musste nicht neu kompiliert werden. Die Anweisung wurde seit X Sekunden im Cache gespeichert. Die Zahl X ist proportional dazu, wie lange die Anwendung bereits läuft und wie lange die Anweisung im Cache ist. Zum Beispiel wäre sie 86400 für einen Zeitraum von 24 Stunden.
Jedes Badge wird unten detaillierter beschrieben.
Die ersten Anweisungen, die wir für das obige Programm sehen, sind die SQLite-Dialektprüfungen auf die Existenz der Tabellen „a“ und „b“
INFO sqlalchemy.engine.Engine PRAGMA temp.table_info("a")
INFO sqlalchemy.engine.Engine [raw sql] ()
INFO sqlalchemy.engine.Engine PRAGMA main.table_info("b")
INFO sqlalchemy.engine.Engine [raw sql] ()
Für die obigen beiden SQLite PRAGMA-Anweisungen lautet das Badge [rawsql], was darauf hindeutet, dass der Treiber einen Python-String direkt über Connection.exec_driver_sql() an die Datenbank sendet. Das Caching ist für solche Anweisungen nicht anwendbar, da sie bereits in String-Form vorliegen und keine Informationen darüber vorliegen, welche Arten von Ergebniszeilen zurückgegeben werden, da SQLAlchemy SQL-Strings nicht im Voraus parst.
Die nächsten Anweisungen, die wir sehen, sind die CREATE TABLE-Anweisungen
Für jede dieser Anweisungen lautet das Badge [nokey0.00006s]. Dies deutet darauf hin, dass bei diesen beiden speziellen Anweisungen kein Caching stattgefunden hat, da das DDL-orientierte CreateTable-Konstrukt keinen Cache-Schlüssel erzeugt hat. DDL-Konstrukte nehmen in der Regel nicht am Caching teil, da sie normalerweise nicht wiederholt werden und DDL auch ein Datenbankkonfigurationsschritt ist, bei dem die Leistung nicht so kritisch ist.
Das Badge [nokey] ist aus einem weiteren Grund wichtig, da es für SQL-Anweisungen erzeugt werden kann, die zwischengespeichert werden können, mit Ausnahme eines bestimmten Unterkonstrukts, der derzeit nicht zwischengespeichert werden kann. Beispiele hierfür sind benutzerdefinierte SQL-Elemente, die keine Caching-Parameter definieren, sowie einige Konstrukte, die beliebig lange und nicht reproduzierbare SQL-Strings erzeugen. Die Hauptbeispiele sind das Values-Konstrukt sowie die Verwendung von „Multivalue-Inserts“ mit der Insert.values()-Methode.
Bis jetzt ist unser Cache noch leer. Die nächsten Anweisungen werden jedoch zwischengespeichert, ein Segment sieht so aus
Oben sehen wir im Wesentlichen zwei eindeutige SQL-Strings; "INSERTINTOa(data)VALUES(?)" und "INSERTINTOb(a_id,data)VALUES(?,?)". Da SQLAlchemy gebundene Parameter für alle Literalwerte verwendet, bleibt der eigentliche SQL-String gleich, auch wenn diese Anweisungen viele Male für verschiedene Objekte wiederholt werden, da die Parameter getrennt sind.
Hinweis
Die obigen beiden Anweisungen werden durch den ORM Unit-of-Work-Prozess generiert und tatsächlich in einem separaten Cache zwischengespeichert, der lokal für jeden Mapper ist. Die Mechanik und Terminologie sind jedoch die gleichen. Der Abschnitt Deaktivieren oder Verwenden eines alternativen Dictionaries zum Zwischenspeichern einiger (oder aller) Anweisungen unten beschreibt, wie benutzerorientierter Code auch auf Anweisungsebene ein alternatives Caching-Container verwenden kann.
Das Caching-Badge, das wir für das erste Vorkommen jeder dieser beiden Anweisungen sehen, ist [generatedin0.00011s]. Dies zeigt an, dass die Anweisung **nicht im Cache war, in 0,00011s zu einem String kompiliert und dann zwischengespeichert wurde**. Wenn wir das Badge [generated] sehen, wissen wir, dass dies einen **Cache-Miss** bedeutet. Dies ist für das erste Vorkommen einer bestimmten Anweisung zu erwarten. Wenn jedoch viele neue [generated]-Badges für eine langlaufende Anwendung beobachtet werden, die im Allgemeinen immer wieder dieselbe Reihe von SQL-Anweisungen verwendet, kann dies ein Zeichen dafür sein, dass der Parameter create_engine.query_cache_size zu klein ist. Wenn eine zwischengespeicherte Anweisung aus dem Cache entfernt wird, da der LRU-Cache weniger genutzte Elemente bereinigt, wird beim nächsten Gebrauch das Badge [generated] angezeigt.
Das Caching-Badge, das wir dann bei nachfolgenden Vorkommen jeder dieser beiden Anweisungen sehen, sieht so aus: [cachedsince0.0003533sago]. Dies bedeutet, dass die Anweisung **im Cache gefunden wurde und ursprünglich vor 0,0003533 Sekunden im Cache platziert wurde**. Es ist wichtig zu beachten, dass, obwohl die Badges [generated] und [cachedsince] sich auf Sekunden beziehen, sie unterschiedliche Bedeutungen haben; im Fall von [generated] ist die Zahl eine ungefähre Zeitmessung, wie lange die Kompilierung der Anweisung gedauert hat, und wird eine extrem kurze Zeitspanne sein. Im Fall von [cachedsince] ist dies die Gesamtzeit, die eine Anweisung im Cache vorhanden ist. Für eine Anwendung, die seit sechs Stunden läuft, kann diese Zahl [cachedsince21600secondsago] lauten, und das ist gut so. Hohe Zahlen für „cached since“ deuten darauf hin, dass diese Anweisungen seit langem keinen Cache-Misses mehr hatten. Anweisungen, die trotz langer Laufzeit der Anwendung häufig niedrige „cached since“-Werte aufweisen, können darauf hindeuten, dass diese Anweisungen zu häufigen Cache-Misses ausgesetzt sind und dass die Größe create_engine.query_cache_size möglicherweise erhöht werden muss.
Unser Beispielprogramm führt dann einige SELECTs durch, bei denen wir das gleiche Muster „generated“ gefolgt von „cached“ für den SELECT der Tabelle „a“ sowie für nachfolgende Lazy Loads der Tabelle „b“ sehen können
INFO sqlalchemy.engine.Engine SELECT a.id AS a_id, a.data AS a_data
FROM a
INFO sqlalchemy.engine.Engine [generated in 0.00009s] ()
INFO sqlalchemy.engine.Engine SELECT b.id AS b_id, b.a_id AS b_a_id, b.data AS b_data
FROM b
WHERE ? = b.a_id
INFO sqlalchemy.engine.Engine [generated in 0.00010s] (1,)
INFO sqlalchemy.engine.Engine SELECT b.id AS b_id, b.a_id AS b_a_id, b.data AS b_data
FROM b
WHERE ? = b.a_id
INFO sqlalchemy.engine.Engine [cached since 0.0005922s ago] (2,)
INFO sqlalchemy.engine.Engine SELECT b.id AS b_id, b.a_id AS b_a_id, b.data AS b_data
FROM b
WHERE ? = b.a_id
Eine vollständige Ausführung unseres obigen Programms zeigt insgesamt vier verschiedene SQL-Strings, die zwischengespeichert werden. Dies deutet darauf hin, dass eine Cache-Größe von **vier** ausreichen würde. Dies ist offensichtlich eine extrem kleine Größe, und die Standardgröße von 500 kann bei ihrem Standardwert belassen werden.
Der vorherige Abschnitt beschrieb einige Techniken, um zu überprüfen, ob die Größe create_engine.query_cache_size größer sein muss. Woher wissen wir, ob der Cache nicht zu groß ist? Der Grund, warum wir create_engine.query_cache_size nicht auf eine bestimmte Zahl über diesem Wert setzen wollen, wäre, weil wir eine Anwendung haben, die eine sehr große Anzahl verschiedener Anweisungen verwenden könnte, wie z. B. eine Anwendung, die Abfragen aus einer Such-UX im laufenden Betrieb erstellt, und wir nicht möchten, dass unser Host den Speicher verbraucht, wenn beispielsweise hunderttausend verschiedene Abfragen in den letzten 24 Stunden ausgeführt wurden und alle zwischengespeichert wurden.
Es ist äußerst schwierig zu messen, wie viel Speicher von Python-Datenstrukturen belegt wird. Mit einem Prozess zur Messung des Speicherwachstums über top, während sukzessive 250 neue Anweisungen zum Cache hinzugefügt werden, deutet ein moderates Core-Statement auf etwa 12 KB hin, während ein kleines ORM-Statement etwa 20 KB beansprucht, einschließlich Ergebnis-Abruf-Strukturen, die für das ORM deutlich größer sind.
Deaktivieren oder Verwenden eines alternativen Dictionaries zum Zwischenspeichern einiger (oder aller) Anweisungen¶
Der interne Cache wird als LRUCache bezeichnet, aber das ist hauptsächlich nur ein Dictionary. Jedes Dictionary kann als Cache für eine beliebige Anzahl von Anweisungen verwendet werden, indem die Option Connection.execution_options.compiled_cache als Ausführungsoption verwendet wird. Ausführungsoptionen können für eine Anweisung, eine Engine oder eine Connection festgelegt werden, sowie bei Verwendung der ORM-Methode Session.execute() für Aufrufe im SQLAlchemy-2.0-Stil. Um beispielsweise eine Reihe von SQL-Anweisungen auszuführen und sie in einem bestimmten Dictionary zwischenzuspeichern
Die SQLAlchemy ORM verwendet die obige Technik, um Mapper-spezifische Caches im Unit-of-Work-„Flush“-Prozess zu halten, die sich vom Standardcache unterscheiden, der auf der Engine konfiguriert ist, sowie für einige Relationship-Loader-Queries.
Der Cache kann auch mit diesem Argument deaktiviert werden, indem der Wert None übergeben wird
# disable caching for this connectionwithengine.connect().execution_options(compiled_cache=None)asconn:conn.execute(table.select())
Die Caching-Funktion erfordert, dass der Compiler des Dialekts SQL-Strings erzeugt, die für viele Anweisungsaufrufe wiederverwendbar sind, unter Verwendung eines bestimmten Cache-Schlüssels, der auf diesen SQL-String abgebildet ist. Das bedeutet, dass beliebige Literalwerte in einer Anweisung, wie z. B. die LIMIT/OFFSET-Werte für ein SELECT, nicht fest im Kompilierungsschema des Dialekts codiert werden können, da der kompilierte String nicht wiederverwendbar ist. SQLAlchemy unterstützt gerenderte gebundene Parameter mit der Methode BindParameter.render_literal_execute(), die auf die vorhandenen Attribute Select._limit_clause und Select._offset_clause eines benutzerdefinierten Compilers angewendet werden kann, was später in diesem Abschnitt illustriert wird.
Da es viele Drittanbieter-Dialekte gibt, von denen viele möglicherweise Literalwerte aus SQL-Anweisungen generieren, ohne von der neueren „Literal Execute“-Funktion zu profitieren, hat SQLAlchemy ab Version 1.4.5 ein Attribut für Dialekte namens Dialect.supports_statement_cache hinzugefügt. Dieses Attribut wird zur Laufzeit auf dessen Vorhandensein direkt in der Klasse eines bestimmten Dialekts überprüft, auch wenn es bereits in einer Oberklasse vorhanden ist. Daher muss auch ein Drittanbieter-Dialekt, der einen bestehenden, cachebaren SQLAlchemy-Dialekt wie sqlalchemy.dialects.postgresql.PGDialect untererbt, dieses Attribut explizit einschließen, damit das Caching aktiviert wird. Das Attribut sollte **nur** aktiviert werden, sobald der Dialekt entsprechend angepasst und auf die Wiederverwendbarkeit kompilierter SQL-Anweisungen mit unterschiedlichen Parametern getestet wurde.
Für alle Drittanbieter-Dialekte, die dieses Attribut nicht unterstützen, wird die Protokollierung für einen solchen Dialekt anzeigen dialectdoesnotsupportcaching.
Wenn ein Dialekt gegen das Caching getestet wurde und insbesondere der SQL-Compiler so aktualisiert wurde, dass er keine literalen LIMIT/OFFSET-Werte direkt in einem SQL-String rendert, können Dialektautoren das Attribut wie folgt anwenden
Beispiel: Rendern von LIMIT/OFFSET mit Post-Compile-Parametern¶
Angenommen, ein Dialekt überschreibt die Methode SQLCompiler.limit_clause(), die die „LIMIT / OFFSET“-Klausel für eine SQL-Anweisung erzeugt, wie folgt:
Die obige Routine rendert die Integer-Werte Select._limit und Select._offset als literale Integer, die in die SQL-Anweisung eingebettet sind. Dies ist eine gängige Anforderung für Datenbanken, die keinen gebundenen Parameter innerhalb der LIMIT/OFFSET-Klauseln einer SELECT-Anweisung verwenden können. Das Rendern des Integer-Werts in der anfänglichen Kompilierungsphase ist jedoch direkt **inkompatibel** mit dem Caching, da die Limit- und Offset-Integer-Werte eines Select-Objekts nicht Teil des Cache-Schlüssels sind, so dass viele Select-Anweisungen mit unterschiedlichen Limit/Offset-Werten nicht mit dem korrekten Wert gerendert würden.
Die Korrektur für den obigen Code besteht darin, das literale Integer in das „Post-Compile“-Facility von SQLAlchemy zu verschieben, das das literale Integer außerhalb der anfänglichen Kompilierungsphase rendert, aber stattdessen zur Ausführungszeit, bevor die Anweisung an die DBAPI gesendet wird. Dies wird innerhalb der Kompilierungsphase über die Methode BindParameter.render_literal_execute() in Verbindung mit der Verwendung der Attribute Select._limit_clause und Select._offset_clause, die LIMIT/OFFSET als vollständige SQL-Ausdrücke darstellen, wie folgt zugegriffen:
# 1.4 cache-compatible codedeflimit_clause(self,select,**kw):text=""limit_clause=select._limit_clauseoffset_clause=select._offset_clauseifselect._simple_int_clause(limit_clause):text+=" \n LIMIT %s"%(self.process(limit_clause.render_literal_execute(),**kw))eliflimit_clauseisnotNone:# assuming the DB doesn't support SQL expressions for LIMIT.# Otherwise render here normallyraiseexc.CompileError("dialect 'mydialect' can only render simple integers for LIMIT")ifselect._simple_int_clause(offset_clause):text+=" \n OFFSET %s"%(self.process(offset_clause.render_literal_execute(),**kw))elifoffset_clauseisnotNone:# assuming the DB doesn't support SQL expressions for OFFSET.# Otherwise render here normallyraiseexc.CompileError("dialect 'mydialect' can only render simple integers for OFFSET")returntext
Der obige Ansatz generiert eine kompilierte SELECT-Anweisung, die wie folgt aussieht
Wo oben die Indikatoren __[POSTCOMPILE_param_1] und __[POSTCOMPILE_param_2] zur Zeit der Anweisungsausführung mit ihren entsprechenden Ganzzahlwerten gefüllt werden, nachdem der SQL-String aus dem Cache abgerufen wurde.
Nachdem Änderungen wie die oben genannten nach Bedarf vorgenommen wurden, sollte die Flagge Dialect.supports_statement_cache auf True gesetzt werden. Es wird dringend empfohlen, dass Dialekte von Drittanbietern die Testsuite für Dialekte von Drittanbietern verwenden, die sicherstellt, dass Operationen wie SELECTs mit LIMIT/OFFSET korrekt gerendert und zwischengespeichert werden.
Verwendung von Lambdas zur Erzielung signifikanter Geschwindigkeitssteigerungen bei der Statement-Erstellung¶
Deep Alchemy
Diese Technik ist im Allgemeinen nicht essenziell, außer in sehr leistungskritischen Szenarien, und richtet sich an erfahrene Python-Programmierer. Obwohl ziemlich geradlinig, beinhaltet sie Metaprogrammierungskonzepte, die für unerfahrene Python-Entwickler nicht geeignet sind. Der Lambda-Ansatz kann zu einem späteren Zeitpunkt mit minimalem Aufwand auf bestehenden Code angewendet werden.
Python-Funktionen, typischerweise als Lambdas ausgedrückt, können verwendet werden, um SQL-Ausdrücke zu generieren, die basierend auf dem Python-Code-Speicherort der Lambda-Funktion selbst sowie den Closure-Variablen innerhalb des Lambdas zwischengespeichert werden können. Die Begründung dafür ist, nicht nur die SQL-String-kompilierte Form eines SQL-Ausdrucks-Konstrukts zu cachen, wie es das normale Verhalten von SQLAlchemy ist, wenn das Lambda-System nicht verwendet wird, sondern auch die In-Python-Komposition des SQL-Ausdrucks-Konstrukts selbst, was ebenfalls einen gewissen Python-Overhead mit sich bringt.
Das Lambda-SQL-Ausdrucks-Feature ist als leistungssteigerndes Feature verfügbar und wird auch optional in der with_loader_criteria() ORM-Option verwendet, um ein generisches SQL-Fragment bereitzustellen.
Lambda-Statements werden mit der Funktion lambda_stmt() konstruiert, die eine Instanz von StatementLambdaElement zurückgibt, die selbst ein ausführbares Statement-Konstrukt ist. Zusätzliche Modifikatoren und Kriterien werden dem Objekt über den Python-Additionsoperator + oder alternativ über die Methode StatementLambdaElement.add_criteria(), die mehr Optionen ermöglicht, hinzugefügt.
Es wird davon ausgegangen, dass das Konstrukt lambda_stmt() innerhalb einer umschließenden Funktion oder Methode aufgerufen wird, die erwartet wird, in einer Anwendung mehrmals verwendet zu werden, sodass nachfolgende Ausführungen über die erste hinaus die zwischengespeicherte kompilierte SQL-Form nutzen können. Wenn das Lambda innerhalb einer umschließenden Funktion in Python konstruiert wird, unterliegt es auch Closure-Variablen, die für den gesamten Ansatz von Bedeutung sind.
Oben werden die drei lambda-Aufrufe, die zur Definition der Struktur eines SELECT-Statements verwendet werden, genau einmal aufgerufen und der resultierende SQL-String im Kompilierungs-Cache der Engine gespeichert. Von da an kann die Funktion run_my_statement() beliebig oft aufgerufen werden und die darin enthaltenen lambda-Aufrufe werden nicht ausgeführt, sondern nur als Cache-Schlüssel verwendet, um das bereits kompilierte SQL abzurufen.
Hinweis
Es ist wichtig zu beachten, dass bereits ein SQL-Caching vorhanden ist, wenn das Lambda-System nicht verwendet wird. Das Lambda-System fügt nur eine zusätzliche Arbeitsreduktionsschicht pro aufgerufenem SQL-Statement hinzu, indem es das Erstellen des SQL-Konstrukts selbst zwischenspeichert und einen einfacheren Cache-Schlüssel verwendet.
Vor allem liegt der Schwerpunkt im Lambda-SQL-System darauf, sicherzustellen, dass es niemals eine Diskrepanz zwischen dem für ein Lambda generierten Cache-Schlüssel und dem von ihm erzeugten SQL-String gibt. Das LambdaElement und verwandte Objekte führen das gegebene Lambda aus und analysieren es, um zu berechnen, wie es bei jeder Ausführung zwischengespeichert werden soll, und versuchen, potenzielle Probleme zu erkennen. Grundlegende Richtlinien beinhalten
Jede Art von Statement wird unterstützt - während erwartet wird, dass select()-Konstrukte der primäre Anwendungsfall für lambda_stmt() sind, sind DML-Statements wie insert() und update() gleichermaßen verwendbar
ORM-Anwendungsfälle direkt unterstützt - das lambda_stmt() kann ORM-Funktionalität vollständig aufnehmen und direkt mit Session.execute() verwendet werden
Gebundene Parameter werden automatisch berücksichtigt - im Gegensatz zum früheren "baked query"-System von SQLAlchemy berücksichtigt das Lambda-SQL-System Python-Literalwerte, die automatisch zu SQL-gebundenen Parametern werden. Das bedeutet, dass auch wenn ein bestimmtes Lambda nur einmal ausgeführt wird, die Werte, die zu gebundenen Parametern werden, bei jeder Ausführung aus dem Closure des Lambdas extrahiert werden
Oben extrahierte das StatementLambdaElement die Werte von x und y aus dem Closure des Lambdas, das jedes Mal generiert wird, wenn my_stmt() aufgerufen wird; diese wurden in das zwischengespeicherte SQL-Konstrukt als Parameterwerte eingesetzt.
Das Lambda sollte idealerweise in allen Fällen eine identische SQL-Struktur erzeugen - Vermeiden Sie die Verwendung von bedingten Anweisungen oder benutzerdefinierten Aufrufen innerhalb von Lambdas, die dazu führen könnten, dass es basierend auf den Eingaben unterschiedliches SQL erzeugt; wenn eine Funktion bedingt zwei verschiedene SQL-Fragmente verwenden kann, verwenden Sie zwei separate Lambdas
# **Don't** do this:defmy_stmt(parameter,thing=False):stmt=lambda_stmt(lambda:select(table))stmt+=lambdas:(s.where(table.c.x>parameter)ifthingelses.where(table.c.y==parameter))returnstmt# **Do** do this:defmy_stmt(parameter,thing=False):stmt=lambda_stmt(lambda:select(table))ifthing:stmt+=lambdas:s.where(table.c.x>parameter)else:stmt+=lambdas:s.where(table.c.y==parameter)returnstmt
Es gibt eine Vielzahl von Fehlern, die auftreten können, wenn das Lambda kein konsistentes SQL-Konstrukt erzeugt und einige davon derzeit nicht trivial erkennbar sind.
Verwenden Sie keine Funktionen innerhalb des Lambdas zur Erzeugung gebundener Werte - der Ansatz zur Verfolgung gebundener Werte erfordert, dass der tatsächliche Wert, der in der SQL-Anweisung verwendet werden soll, lokal im Closure des Lambdas vorhanden ist. Dies ist nicht möglich, wenn Werte aus anderen Funktionen generiert werden, und das LambdaElement sollte normalerweise einen Fehler auslösen, wenn dies versucht wird
>>> defmy_stmt(x,y):... defget_x():... returnx...... defget_y():... returny...... stmt=lambda_stmt(lambda:select(func.max(get_x(),get_y())))... returnstmt>>> withengine.connect()asconn:... print(conn.scalar(my_stmt(5,10)))Traceback (most recent call last): # ...sqlalchemy.exc.InvalidRequestError: Can't invoke Python callable get_x()inside of lambda expression argument at<code object <lambda> at 0x7fed15f350e0, file "<stdin>", line 6>;lambda SQL constructs should not invoke functions from closure variablesto produce literal values since the lambda SQL system normally extractsbound values without actually invoking the lambda or any functions within it.
Oben sollten die Verwendungen von get_x() und get_y(), falls sie notwendig sind, außerhalb des Lambdas erfolgen und einer lokalen Closure-Variable zugewiesen werden.
Vermeiden Sie Verweise auf Nicht-SQL-Konstrukte innerhalb von Lambdas, da diese standardmäßig nicht zwischengespeichert werden können - dieses Problem bezieht sich darauf, wie das LambdaElement einen Cache-Schlüssel aus anderen Closure-Variablen innerhalb des Statements erstellt. Um die beste Garantie für einen genauen Cache-Schlüssel zu bieten, werden alle im Closure des Lambdas befindlichen Objekte als bedeutend erachtet und keines wird standardmäßig als für einen Cache-Schlüssel geeignet angenommen. Das folgende Beispiel wird ebenfalls eine ziemlich detaillierte Fehlermeldung auslösen.
>>> classFoo:... def__init__(self,x,y):... self.x=x... self.y=y>>> defmy_stmt(foo):... stmt=lambda_stmt(lambda:select(func.max(foo.x,foo.y)))... returnstmt>>> withengine.connect()asconn:... print(conn.scalar(my_stmt(Foo(5,10))))Traceback (most recent call last): # ...sqlalchemy.exc.InvalidRequestError: Closure variable named 'foo' inside oflambda callable <code object <lambda> at 0x7fed15f35450, file"<stdin>", line 2> does not refer to a cacheable SQL element, and alsodoes not appear to be serving as a SQL literal bound value based on thedefault SQL expression returned by the function. This variable needs toremain outside the scope of a SQL-generating lambda so that a proper cachekey may be generated from the lambda's state. Evaluate this variableoutside of the lambda, set track_on=[<elements>] to explicitly selectclosure elements to track, or set track_closure_variables=False to excludeclosure variables from being part of the cache key.
Der obige Fehler zeigt an, dass das LambdaElement nicht davon ausgeht, dass das übergebene Foo-Objekt in allen Fällen gleich funktioniert. Es geht auch nicht davon aus, dass es Foo standardmäßig als Teil des Cache-Schlüssels verwenden kann; wenn es das Foo-Objekt als Teil des Cache-Schlüssels verwenden würde und es viele verschiedene Foo-Objekte gäbe, würde dies den Cache mit doppelten Informationen füllen und auch langfristige Referenzen auf all diese Objekte halten.
Der beste Weg, die obige Situation zu lösen, ist, foo nicht innerhalb des Lambdas zu referenzieren, sondern stattdessen außerhalb.
In einigen Fällen, wenn die SQL-Struktur des Lambdas garantiert niemals auf Basis der Eingabe geändert wird, kann track_closure_variables=False übergeben werden, was jede Nachverfolgung von Closure-Variablen außer denen, die für gebundene Parameter verwendet werden, deaktiviert.
Es gibt auch die Option, Objekte zum Element hinzuzufügen, um explizit Teil des Cache-Schlüssels zu bilden, indem der Parameter track_on verwendet wird; die Verwendung dieses Parameters ermöglicht es, dass bestimmte Werte als Cache-Schlüssel dienen, und verhindert auch, dass andere Closure-Variablen berücksichtigt werden. Dies ist nützlich für Fälle, in denen ein Teil des SQL, das konstruiert wird, von einem kontextuellen Objekt irgendeiner Art stammt, das viele verschiedene Werte haben kann. Im folgenden Beispiel deaktiviert das erste Segment der SELECT-Anweisung die Nachverfolgung der Variablen foo, während das zweite Segment explizit self als Teil des Cache-Schlüssels verfolgt.
Die Verwendung von track_on bedeutet, dass die gegebenen Objekte langfristig im internen Cache des Lambdas gespeichert werden und starke Referenzen haben, solange der Cache diese Objekte nicht löscht (standardmäßig wird ein LRU-Schema mit 1000 Einträgen verwendet).
Um einige der Optionen und Verhaltensweisen, die bei Lambda-SQL-Konstrukten auftreten, zu verstehen, ist ein Verständnis des Caching-Systems hilfreich.
SQLAlchemy's Caching-System generiert normalerweise einen Cache-Schlüssel aus einem gegebenen SQL-Ausdruck-Konstrukt, indem es eine Struktur erzeugt, die den gesamten Zustand innerhalb des Konstrukts darstellt.
>>> fromsqlalchemyimportselect,column>>> stmt=select(column("q"))>>> cache_key=stmt._generate_cache_key()>>> print(cache_key)# somewhat paraphrasedCacheKey(key=( '0', <class 'sqlalchemy.sql.selectable.Select'>, '_raw_columns', ( ( '1', <class 'sqlalchemy.sql.elements.ColumnClause'>, 'name', 'q', 'type', ( <class 'sqlalchemy.sql.sqltypes.NullType'>, ), ), ), # a few more elements are here, and many more for a more # complicated SELECT statement),)
Der obige Schlüssel wird im Cache gespeichert, der im Wesentlichen ein Wörterbuch ist, und der Wert ist ein Konstrukt, das unter anderem die Stringform der SQL-Anweisung speichert, in diesem Fall die Phrase "SELECT q". Wir können beobachten, dass selbst für eine extrem kurze Abfrage der Cache-Schlüssel ziemlich ausführlich ist, da er alles darstellen muss, was an dem, was gerendert und potenziell ausgeführt wird, variieren kann.
Das Lambda-Konstruktionssystem erstellt im Gegensatz dazu eine andere Art von Cache-Schlüssel.
>>> fromsqlalchemyimportlambda_stmt>>> stmt=lambda_stmt(lambda:select(column("q")))>>> cache_key=stmt._generate_cache_key()>>> print(cache_key)CacheKey(key=( <code object <lambda> at 0x7fed1617c710, file "<stdin>", line 1>, <class 'sqlalchemy.sql.lambdas.StatementLambdaElement'>,),)
Oben sehen wir einen Cache-Schlüssel, der wesentlich kürzer ist als der des Nicht-Lambda-Statements, und zusätzlich wurde die Erstellung des select(column("q"))-Konstrukts gar nicht erst benötigt; das Python-Lambda selbst enthält ein Attribut namens __code__, das auf ein Python-Codeobjekt verweist, das zur Laufzeit der Anwendung unveränderlich und permanent ist.
Wenn das Lambda auch Closure-Variablen enthält, werden diese im Normalfall, wenn diese Variablen sich auf SQL-Konstrukte wie Spaltenobjekte beziehen, Teil des Cache-Schlüssels; oder wenn sie sich auf Literalwerte beziehen, die gebundene Parameter werden, werden sie in ein separates Element des Cache-Schlüssels eingefügt.
Das obige StatementLambdaElement enthält zwei Lambdas, die sich beide auf die Closure-Variable col beziehen, sodass der Cache-Schlüssel beide Segmente sowie das column()-Objekt repräsentiert.
Das Feature insertmanyvalues ist ein transparent verfügbares Leistungsfeature, das keine Intervention des Endbenutzers erfordert, damit es bei Bedarf stattfindet. Dieser Abschnitt beschreibt die Architektur des Features sowie, wie seine Leistung gemessen und sein Verhalten zur Optimierung der Geschwindigkeit von Massen-INSERT-Statements, insbesondere wie sie vom ORM verwendet werden, abgestimmt werden kann.
Da immer mehr Datenbanken Unterstützung für INSERT..RETURNING hinzugefügt haben, hat SQLAlchemy eine wesentliche Änderung erfahren, wie es mit INSERT-Statements umgeht, bei denen es notwendig ist, serverseitig generierte Werte zu erhalten, insbesondere serverseitig generierte Primärschlüsselwerte, die es ermöglichen, die neue Zeile in nachfolgenden Operationen zu referenzieren. Insbesondere dieses Szenario war lange Zeit ein erhebliches Leistungsproblem im ORM, das darauf angewiesen ist, serverseitig generierte Primärschlüsselwerte abrufen zu können, um die Identitätszuordnung korrekt zu füllen.
Mit der kürzlich hinzugefügten Unterstützung für RETURNING in SQLite und MariaDB muss SQLAlchemy für die meisten Backends nicht mehr auf das nur für einzelne Zeilen vorgesehene Attribut cursor.lastrowid des DBAPI zurückgreifen; RETURNING kann nun für alle SQLAlchemy-inkludierten Backends mit Ausnahme von MySQL verwendet werden. Die verbleibende Leistungseinschränkung, dass die cursor.executemany() DBAPI-Methode das Abrufen von Zeilen nicht erlaubt, wird für die meisten Backends behoben, indem auf die Verwendung von executemany() verzichtet und stattdessen einzelne INSERT-Statements umstrukturiert werden, um jeweils eine große Anzahl von Zeilen in einem einzigen Statement zu ermöglichen, das mit cursor.execute() aufgerufen wird. Dieser Ansatz stammt aus den psycopg2 Fast Execution Helpers des psycopg2 DBAPI, für die SQLAlchemy in den letzten Release-Serien zunehmend mehr Unterstützung hinzugefügt hat.
Das Feature ist für alle in SQLAlchemy enthaltenen Backends aktiviert, die RETURNING unterstützen, mit Ausnahme der Oracle Database, für die sowohl die Python-Treiber python-oracledb als auch cx_Oracle ihre eigenen äquivalenten Features anbieten. Das Feature findet normalerweise statt, wenn die Methode Insert.returning() eines Insert-Konstrukts in Verbindung mit der executemany-Ausführung verwendet wird, was beim Übergeben einer Liste von Dictionaries an den Parameter Connection.execute.parameters der Methoden Connection.execute() oder Session.execute() (sowie äquivalente Methoden unter asyncio und Kurzform-Methoden wie Session.scalars()) geschieht. Es findet auch innerhalb des Unit of Work-Prozesses des ORM statt, wenn Methoden wie Session.add() und Session.add_all() zum Hinzufügen von Zeilen verwendet werden.
Für die von SQLAlchemy mitgelieferten Dialekte ist die Unterstützung oder eine äquivalente Unterstützung derzeit wie folgt:
SQLite - unterstützt für SQLite-Versionen 3.35 und höher
PostgreSQL - alle unterstützten PostgreSQL-Versionen (9 und höher)
SQL Server - alle unterstützten SQL Server-Versionen [1]
MariaDB - unterstützt für MariaDB-Versionen 10.5 und höher
MySQL - keine Unterstützung, kein RETURNING-Feature vorhanden
Oracle Database - unterstützt RETURNING mit executemany unter Verwendung nativer python-oracledb / cx_Oracle APIs für alle unterstützten Oracle Database-Versionen ab 9, unter Verwendung von Multi-Row-OUT-Parametern. Dies ist nicht dasselbe Implementierungsverfahren wie „executemanyvalues“, hat jedoch dieselben Nutzungsmuster und äquivalente Leistungsvorteile.
Das Feature kann auch deaktiviert werden, um es für ein bestimmtes Table-Objekt nicht implizit zu verwenden, indem der Parameter Table.implicit_returning als False übergeben wird.
Das Feature hat zwei Betriebsmodi, die transparent pro Dialekt und pro Table ausgewählt werden. Einer ist der Stapelmodus, der die Anzahl der Datenbank-Roundtrips reduziert, indem er eine INSERT-Anweisung der Form
wobei die Anweisung gegen eine Teilmenge (einen „Stapel“) der Eingabedaten organisiert ist, deren Größe vom Datenbank-Backend sowie der Anzahl der Parameter in jedem Stapel bestimmt wird, um bekannten Grenzen für die Anweisungsgröße / Anzahl der Parameter zu entsprechen. Das Feature führt dann für jeden Stapel von Eingabedaten eine INSERT-Anweisung aus, bis alle Datensätze verbraucht sind, und verkettet die RETURNING-Ergebnisse für jeden Stapel zu einer einzigen großen Zeilenmenge, die von einem einzigen Result-Objekt verfügbar ist.
Diese „gestapelte“ Form ermöglicht das Einfügen vieler Zeilen mit wesentlich weniger Datenbank-Roundtrips und hat sich als dramatische Leistungsverbesserung für die meisten unterstützten Backends erwiesen.
Korrelation von RETURNING-Zeilen mit Parametersätzen¶
Neu in Version 2.0.10.
Die im vorherigen Abschnitt veranschaulichte „Batch“-Modus-Abfrage garantiert nicht, dass die Reihenfolge der zurückgegebenen Datensätze der Reihenfolge der Eingabedaten entspricht. Wenn dies vom SQLAlchemy ORM Unit of Work-Prozess verwendet wird, sowie für Anwendungen, die zurückgegebene serverseitig generierte Werte mit Eingabedaten korrelieren, enthalten die Methoden Insert.returning() und UpdateBase.return_defaults() eine Option Insert.returning.sort_by_parameter_order, die angibt, dass der „insertmanyvalues“-Modus diese Entsprechung garantieren soll. Dies hat nichts mit der Reihenfolge zu tun, in der die Datensätze tatsächlich von der Datenbank-Backend eingefügt werden, was unter keinen Umständen angenommen wird; nur dass die zurückgegebenen Datensätze bei Erhalt so organisiert werden sollten, dass sie der Reihenfolge entsprechen, in der die ursprünglichen Eingabedaten übergeben wurden.
Wenn der Parameter Insert.returning.sort_by_parameter_order vorhanden ist, können für Tabellen, die serverseitig generierte Integer-Primärschlüsselwerte wie IDENTITY, PostgreSQL SERIAL, MariaDB AUTO_INCREMENT oder das ROWID-Schema von SQLite verwenden, im „Batch“-Modus eine komplexere INSERT..RETURNING-Form verwendet werden, in Verbindung mit einer nachträglichen Sortierung der Zeilen basierend auf den zurückgegebenen Werten. Oder wenn eine solche Form nicht verfügbar ist, kann das „insertmanyvalues“-Feature nahtlos in den „nicht-gestapelten“ Modus übergehen, der einzelne INSERT-Anweisungen für jeden Parametersatz ausführt.
Zum Beispiel wird bei SQL Server, wenn eine automatisch inkrementierende IDENTITY-Spalte als Primärschlüssel verwendet wird, die folgende SQL-Form verwendet:
Eine ähnliche Form wird auch für PostgreSQL verwendet, wenn Primärschlüsselspalten SERIAL oder IDENTITY verwenden. Die obige Form garantiert nicht die Reihenfolge, in der Zeilen eingefügt werden. Sie garantiert jedoch, dass die IDENTITY- oder SERIAL-Werte in Ordnung mit jedem Parametersatz erzeugt werden [2]. Das Feature „insertmanyvalues“ sortiert dann die zurückgegebenen Zeilen für die obige INSERT-Anweisung nach steigender Integer-Identität.
Für die SQLite-Datenbank gibt es keine geeignete INSERT-Form, die die Erzeugung neuer ROWID-Werte mit der Reihenfolge korrelieren kann, in der die Parametersätze übergeben werden. Infolgedessen wird bei der Verwendung von serverseitig generierten Primärschlüsselwerten das SQLite-Backend in den „nicht-gestapelten“ Modus übergehen, wenn eine geordnete RETURNING-Anforderung gestellt wird. Für MariaDB ist die von insertmanyvalues verwendete Standard-INSERT-Form ausreichend, da dieses Datenbank-Backend die Reihenfolge von AUTO_INCREMENT mit der Reihenfolge der Eingabedaten abgleicht, wenn InnoDB verwendet wird [3].
Für einen clientseitig generierten Primärschlüssel, z. B. bei Verwendung der Python-Funktion uuid.uuid4() zum Generieren neuer Werte für eine Uuid-Spalte, schließt das „insertmanyvalues“-Feature transparent diese Spalte in die RETURNING-Datensätze ein und korreliert ihren Wert mit dem der gegebenen Eingabedatensätze, wodurch die Übereinstimmung zwischen Eingabedatensätzen und Ergebniszeilen aufrechterhalten wird. Daraus folgt, dass alle Backends gestapelte, parameterkorrelierte RETURNING-Reihenfolge ermöglichen, wenn clientseitig generierte Primärschlüsselwerte verwendet werden.
Das Thema, wie der „insertmanyvalues“-„Batch“-Modus eine oder mehrere Spalten zur Korrelation zwischen Eingabeparametern und RETURNING-Zeilen bestimmt, wird als Insert-Sentinel bezeichnet, einer spezifischen Spalte oder mehreren Spalten, die zur Nachverfolgung solcher Werte verwendet werden. Der „Insert-Sentinel“ wird normalerweise automatisch ausgewählt, kann aber auch für extrem spezielle Fälle vom Benutzer konfiguriert werden; der Abschnitt Konfigurieren von Sentinel-Spalten beschreibt dies.
Für Backends, die keine geeignete INSERT-Form anbieten, die serverseitig generierte Werte deterministisch an die Eingabewerte angepasst liefern kann, oder für Table-Konfigurationen mit anderen Arten von serverseitig generierten Primärschlüsselwerten, wird der „insertmanyvalues“-Modus den nicht-gestapelten Modus verwenden, wenn eine garantierte RETURNING-Reihenfolge angefordert wird.
Für Table-Konfigurationen, die keine primären Schlüsselwerte auf Clientseite haben und serverseitig generierte primäre Schlüsselwerte (oder keinen Primärschlüssel) anbieten, die von der betreffenden Datenbank nicht deterministisch oder sortierbar in Bezug auf mehrere Parametersätze aufgerufen werden können, kann die „insertmanyvalues“-Funktion, wenn sie die Anforderung Insert.returning.sort_by_parameter_order für eine Insert-Anweisung erfüllen soll, stattdessen den **nicht-gepufferten Modus** verwenden.
In diesem Modus wird die ursprüngliche SQL-Form von INSERT beibehalten, und die „insertmanyvalues“-Funktion führt die Anweisung stattdessen für jeden einzelnen Parametersatz aus und organisiert die zurückgegebenen Zeilen zu einem vollständigen Ergebnissatz. Im Gegensatz zu früheren SQLAlchemy-Versionen geschieht dies in einer engen Schleife, die den Python-Overhead minimiert. In einigen Fällen, wie z. B. bei SQLite, funktioniert der „nicht-gepufferte“ Modus genauso gut wie der „gepufferte“ Modus.
Sowohl im „gepufferten“ als auch im „nicht-gepufferten“ Modus ruft die Funktion zwangsläufig **mehrere INSERT-Anweisungen** über die DBAPI-Methode cursor.execute() auf, innerhalb des Gültigkeitsbereichs eines **einzigen** Aufrufs der Core-Methode Connection.execute(), wobei jede Anweisung bis zu einer festgelegten Grenze von Parametersätzen enthält. Diese Grenze ist wie unten unter Steuerung der Puffergröße beschrieben konfigurierbar. Die separaten Aufrufe von cursor.execute() werden einzeln protokolliert und einzeln an Listener wie ConnectionEvents.before_cursor_execute() übergeben (siehe Protokollierung und Ereignisse unten).
In typischen Fällen bestimmt die Funktion „insertmanyvalues“, um INSERT..RETURNING mit deterministischer Zeilenreihenfolge bereitzustellen, automatisch eine Sentinel-Spalte aus dem Primärschlüssel einer gegebenen Tabelle und stuft bei Nichterkennbarkeit gnädig auf den Modus „Zeile für Zeile“ zurück. Als völlig **optionale** Funktion können für Tabellen mit serverseitig generierten Primärschlüsseln, deren Standard-Generatorfunktionen nicht mit dem „Sentinel“-Anwendungsfall kompatibel sind, andere Nicht-Primärschlüsselspalten als „Sentinel“-Spalten markiert werden, vorausgesetzt, sie erfüllen bestimmte Anforderungen, um die volle „insertmanyvalues“-Bulk-Leistung zu erzielen. Ein typisches Beispiel ist eine Uuid-Spalte ohne Primärschlüssel mit einem Standardwert auf Clientseite, wie z. B. die Python-Funktion uuid.uuid4(). Es gibt auch ein Konstrukt zur Erstellung einfacher Ganzzahlspalten mit einem auf den „insertmanyvalues“-Anwendungsfall ausgerichteten Ganzzahlzähler auf Clientseite.
Sentinel-Spalten können durch Hinzufügen von Column.insert_sentinel zu qualifizierenden Spalten angegeben werden. Die grundlegendste „qualifizierende“ Spalte ist eine nicht-nullable, eindeutige Spalte mit einem Standardwert auf Clientseite, wie z. B. eine UUID-Spalte, wie folgt:
importuuidfromsqlalchemyimportColumnfromsqlalchemyimportFetchedValuefromsqlalchemyimportIntegerfromsqlalchemyimportStringfromsqlalchemyimportTablefromsqlalchemyimportUuidmy_table=Table("some_table",metadata,# assume some arbitrary server-side function generates# primary key values, so cannot be tracked by a bulk insertColumn("id",String(50),server_default=FetchedValue(),primary_key=True),Column("data",String(50)),Column("uniqueid",Uuid(),default=uuid.uuid4,nullable=False,unique=True,insert_sentinel=True,),)
Bei Verwendung von ORM-deklarativen Modellen sind die gleichen Formen über das mapped_column-Konstrukt verfügbar.
Während die vom Standardgenerator erzeugten Werte eindeutig sein **müssen**, ist die tatsächliche UNIQUE-Beschränkung auf der obigen „Sentinel“-Spalte, die durch den Parameter unique=True angegeben wird, optional und kann bei Bedarf weggelassen werden.
Es gibt auch eine spezielle Form von „insert sentinel“, nämlich eine dedizierte, nullable Ganzzahlspalte, die einen speziellen Standard-Ganzzahlzähler verwendet, der nur während „insertmanyvalues“-Operationen verwendet wird; als zusätzliche Funktion wird die Spalte von SQL-Anweisungen und Ergebnissätzen ausgeschlossen und verhält sich weitgehend transparent. Sie muss jedoch physisch in der tatsächlichen Datenbanktabelle vorhanden sein. Dieser Spaltentyp Column kann mit der Funktion insert_sentinel() erstellt werden.
Bei Verwendung von ORM-Deklarationen ist eine deklarationsfreundliche Version von insert_sentinel() namens orm_insert_sentinel() verfügbar, die auf die Basisklasse oder ein Mixin angewendet werden kann; wenn sie mit declared_attr() verpackt ist, wird die Spalte auf alle tabellengebundenen Unterklassen angewendet, auch innerhalb von verketteten Vererbungshierarchien.
Im obigen Beispiel erhalten sowohl „my_table“ als auch „sub_table“ eine zusätzliche Ganzzahlspalte namens „_sentinel“, die von der „insertmanyvalues“-Funktion zur Optimierung von Massen-INSERTs verwendet werden kann, die vom ORM verwendet werden.
Ein Hauptmerkmal von „insertmanyvalues“ ist, dass die Größe der INSERT-Anweisung auf eine feste maximale Anzahl von „VALUES“-Klauseln sowie eine dialektspezifische feste Gesamtzahl von gebundenen Parametern begrenzt ist, die in einer INSERT-Anweisung gleichzeitig dargestellt werden können. Wenn die Anzahl der gegebenen Parameterwörterbücher ein festes Limit überschreitet oder wenn die Gesamtzahl der in einer einzelnen INSERT-Anweisung darzustellenden gebundenen Parameter ein festes Limit überschreitet (die beiden festen Limits sind getrennt), werden mehrere INSERT-Anweisungen im Rahmen eines einzelnen Connection.execute()-Aufrufs ausgeführt, die jeweils einen Teil der Parameterwörterbücher aufnehmen, bekannt als „Batch“. Die Anzahl der Parameterwörterbücher, die in jedem „Batch“ dargestellt werden, wird dann als „Batch-Größe“ bezeichnet. Eine Batch-Größe von 500 bedeutet beispielsweise, dass jede ausgegebene INSERT-Anweisung höchstens 500 Zeilen einfügt.
Es kann wichtig sein, die Batch-Größe anzupassen, da eine größere Batch-Größe für einen INSERT, bei dem die Wertesätze selbst relativ klein sind, performanter sein kann, und eine kleinere Batch-Größe für einen INSERT, der sehr große Wertesätze verwendet, geeigneter sein kann, wo sowohl die Größe des gerenderten SQL als auch die Gesamtgröße der in einer Anweisung übergebenen Daten von einer bestimmten Größe basierend auf dem Backend-Verhalten und Speicherbeschränkungen profitieren können. Aus diesem Grund kann die Batch-Größe pro Engine sowie pro Anweisung konfiguriert werden. Das Parameterlimit hingegen ist fixiert, basierend auf den bekannten Merkmalen der verwendeten Datenbank.
Die Batch-Größe beträgt standardmäßig 1000 für die meisten Backends, mit einem zusätzlichen dialektspezifischen Faktor „maximale Anzahl von Parametern“, der die Batch-Größe pro Anweisung weiter reduzieren kann. Die maximale Anzahl von Parametern variiert je nach Dialekt und Serverversion; die größte Größe ist 32700 (gewählt als gesunder Abstand zu PostgreSQLs Limit von 32767 und SQLite's modernem Limit von 32766, während Platz für zusätzliche Parameter in der Anweisung sowie für DBAPI-Eigenheiten bleibt). Ältere SQLite-Versionen (vor 3.32.0) setzen diesen Wert auf 999. MariaDB hat keine etablierte Grenze, jedoch bleibt 32700 als einschränkender Faktor für die SQL-Nachrichtengröße.
Der Wert der „Batch-Größe“ kann Engine-weit über den Parameter create_engine.insertmanyvalues_page_size beeinflusst werden. Zum Beispiel, um INSERT-Anweisungen so zu beeinflussen, dass sie bis zu 100 Parametersätze pro Anweisung enthalten.
Die Funktion „insertmanyvalues“ lässt sich vollständig in die Anweisungsprotokollierung von SQLAlchemy sowie in Cursor-Ereignisse wie ConnectionEvents.before_cursor_execute() integrieren. Wenn die Parameterliste in separate Batches aufgeteilt wird, wird **jede INSERT-Anweisung einzeln protokolliert und an Ereignisbehandler übergeben**. Dies ist eine wesentliche Änderung gegenüber der Funktion, die nur für psycopg2 in früheren SQLAlchemy-Versionen der 1.x-Reihe verfügbar war, bei der die Erstellung mehrerer INSERT-Anweisungen vor der Protokollierung und den Ereignissen verborgen war. Die Protokollanzeige kürzt lange Parameterlisten zur besseren Lesbarkeit und zeigt auch den spezifischen Batch jeder Anweisung an. Das folgende Beispiel zeigt einen Ausschnitt dieser Protokollierung.
INSERT INTO a (data, x, y) VALUES (?, ?, ?), ... 795 characters truncated ... (?, ?, ?), (?, ?, ?) RETURNING id
[generated in 0.00177s (insertmanyvalues) 1/10 (unordered)] ('d0', 0, 0, 'd1', ...
INSERT INTO a (data, x, y) VALUES (?, ?, ?), ... 795 characters truncated ... (?, ?, ?), (?, ?, ?) RETURNING id
[insertmanyvalues 2/10 (unordered)] ('d100', 100, 1000, 'd101', ...
...
INSERT INTO a (data, x, y) VALUES (?, ?, ?), ... 795 characters truncated ... (?, ?, ?), (?, ?, ?) RETURNING id
[insertmanyvalues 10/10 (unordered)] ('d900', 900, 9000, 'd901', ...
Wenn der nicht-gepufferte Modus stattfindet, wird dies zusammen mit der insertmanyvalues-Nachricht in der Protokollierung angezeigt.
...
INSERT INTO a (data, x, y) VALUES (?, ?, ?) RETURNING id
[insertmanyvalues 67/78 (ordered; batch not supported)] ('d66', 66, 66)
INSERT INTO a (data, x, y) VALUES (?, ?, ?) RETURNING id
[insertmanyvalues 68/78 (ordered; batch not supported)] ('d67', 67, 67)
INSERT INTO a (data, x, y) VALUES (?, ?, ?) RETURNING id
[insertmanyvalues 69/78 (ordered; batch not supported)] ('d68', 68, 68)
INSERT INTO a (data, x, y) VALUES (?, ?, ?) RETURNING id
[insertmanyvalues 70/78 (ordered; batch not supported)] ('d69', 69, 69)
...
Die Dialekte PostgreSQL, SQLite und MariaDB bieten backend-spezifische „Upsert“-Konstrukte insert(), insert() und insert(), die jeweils Insert-Konstrukte sind, die über eine zusätzliche Methode wie on_conflict_do_update()oder``on_duplicate_key() verfügen. Diese Konstrukte unterstützen auch „insertmanyvalues“-Verhalten, wenn sie mit RETURNING verwendet werden, was effiziente Upserts mit RETURNING ermöglicht.
Die Engine bezieht sich auf einen Verbindungspool, was bedeutet, dass unter normalen Umständen offene Datenbankverbindungen vorhanden sind, solange das Engine-Objekt noch im Speicher resident ist. Wenn eine Engine von der Garbage Collection bereinigt wird, wird ihr Verbindungspool nicht mehr von dieser Engine referenziert, und vorausgesetzt, keine ihrer Verbindungen wird noch ausgeliehen, werden der Pool und seine Verbindungen ebenfalls von der Garbage Collection bereinigt, was zur Schließung der tatsächlichen Datenbankverbindungen führt. Ansonsten behält die Engine offene Datenbankverbindungen, vorausgesetzt, sie verwendet die normalerweise standardmäßige Pool-Implementierung von QueuePool.
Die Engine ist dazu bestimmt, normalerweise eine permanente Einrichtung zu sein, die im Voraus erstellt und während der gesamten Lebensdauer einer Anwendung beibehalten wird. Sie ist **nicht** dazu bestimmt, pro Verbindung erstellt und entsorgt zu werden; stattdessen ist sie eine Registrierung, die sowohl einen Pool von Verbindungen als auch Konfigurationsinformationen über die verwendete Datenbank und DBAPI sowie einen gewissen Grad an internem Cache für datenbankspezifische Ressourcen pflegt.
Es gibt jedoch viele Fälle, in denen es wünschenswert ist, dass alle von der Engine referenzierten Verbindungsressourcen vollständig geschlossen werden. Es ist im Allgemeinen keine gute Idee, sich auf die Garbage Collection von Python zu verlassen, damit dies in diesen Fällen geschieht; stattdessen kann die Engine explizit mit der Methode Engine.dispose() entsorgt werden. Dies entsorgt den zugrunde liegenden Verbindungspool der Engine und ersetzt ihn durch einen neuen, leeren Pool. Vorausgesetzt, die Engine wird zu diesem Zeitpunkt verworfen und nicht mehr verwendet, werden auch alle von ihr referenzierten **eingecheckten** Verbindungen vollständig geschlossen.
Gültige Anwendungsfälle für den Aufruf von Engine.dispose() sind:
Wenn ein Programm alle verbleibenden eingecheckten Verbindungen freigeben möchte, die vom Verbindungspool gehalten werden, und erwartet, dass es für zukünftige Operationen überhaupt keine Verbindung mehr zu dieser Datenbank hat.
Wenn ein Programm Multiprocessing oder fork() verwendet und ein Engine-Objekt in den Kindprozess kopiert wird, sollte Engine.dispose() aufgerufen werden, damit die Engine brandneue, lokale Datenbankverbindungen für diesen Fork erstellt. Datenbankverbindungen reisen im Allgemeinen **nicht** über Prozessgrenzen hinweg. Verwenden Sie in diesem Fall den Parameter Engine.dispose.close auf False gesetzt. Weitere Einzelheiten zu diesem Anwendungsfall finden Sie im Abschnitt Verwendung von Verbindungspools mit Multiprocessing oder os.fork().
Innerhalb von Testsuiten oder Multitenancy-Szenarien, in denen viele Ad-hoc- und kurzlebige Engine-Objekte erstellt und entsorgt werden können.
Verbindungen, die **ausgecheckt** sind, werden bei der Entsorgung oder Garbage Collection der Engine **nicht** verworfen, da diese Verbindungen noch woanders stark von der Anwendung referenziert werden. Nachdem jedoch Engine.dispose() aufgerufen wurde, sind diese Verbindungen nicht mehr mit dieser Engine verbunden; wenn sie geschlossen werden, werden sie zu ihrem nun verwaisten Verbindungspool zurückgegeben, der schließlich von der Garbage Collection bereinigt wird, sobald alle ihn referenzierenden Verbindungen nicht mehr referenziert werden. Da dieser Prozess nicht einfach zu steuern ist, wird dringend empfohlen, Engine.dispose() nur aufzurufen, nachdem alle ausgecheckten Verbindungen eingecheckt oder anderweitig von ihrem Pool getrennt wurden.
Eine Alternative für Anwendungen, die negativ durch die Verwendung von Verbindungspooling durch die Engine-Objekte beeinträchtigt werden, ist das vollständige Deaktivieren des Poolings. Dies verursacht in der Regel nur einen geringen Leistungsabfall beim Erstellen neuer Verbindungen, und bedeutet, dass beim Einchecken einer Verbindung diese vollständig geschlossen und nicht im Speicher gehalten wird. Richtlinien zur Deaktivierung des Poolings finden Sie im Abschnitt Auswahl von Pool-Implementierungen.
Arbeiten mit Treiber-SQL und rohen DBAPI-Verbindungen¶
Die Einführung in die Verwendung von Connection.execute() verwendete das text()-Konstrukt, um zu veranschaulichen, wie Text-SQL-Anweisungen aufgerufen werden können. Beim Arbeiten mit SQLAlchemy sind Text-SQL tatsächlich eher die Ausnahme als die Regel, da die Core-Ausdruckssprache und das ORM beide die Textdarstellung von SQL abstrahieren. Das text()-Konstrukt selbst bietet jedoch auch eine gewisse Abstraktion von Text-SQL, indem es normalisiert, wie gebundene Parameter übergeben werden, und indem es Datentypverhalten für Parameter und Ergebnismengenzeilen unterstützt.
Für den Anwendungsfall, bei dem man Text-SQL direkt an den zugrunde liegenden Treiber (bekannt als DBAPI) übergeben möchte, ohne jegliche Intervention durch das text()-Konstrukt, kann die Methode Connection.exec_driver_sql() verwendet werden.
Es gibt Fälle, in denen SQLAlchemy keinen generischen Weg zum Zugriff auf einige DBAPI-Funktionen bietet, wie z. B. das Aufrufen von gespeicherten Prozeduren oder das Behandeln mehrerer Ergebnismengen. In diesen Fällen ist es ebenso praktikabel, direkt mit der rohen DBAPI-Verbindung zu arbeiten.
Der gebräuchlichste Weg, auf die rohe DBAPI-Verbindung zuzugreifen, ist, sie direkt von einem bereits vorhandenen Connection-Objekt zu erhalten. Sie ist über das Attribut Connection.connection verfügbar.
Die DBAPI-Verbindung ist hier tatsächlich „proxied“ in Bezug auf den ursprünglichen Verbindungspool, dies ist jedoch ein Implementierungsdetail, das in den meisten Fällen ignoriert werden kann. Da diese DBAPI-Verbindung immer noch im Gültigkeitsbereich eines besitzenden Connection-Objekts enthalten ist, ist es am besten, die meisten Funktionen wie Transaktionskontrolle sowie den Aufruf der Methode Connection.close() über das Connection-Objekt zu verwenden; wenn diese Operationen direkt auf der DBAPI-Verbindung ausgeführt werden, wird die besitzende Connection diese Zustandsänderungen nicht erkennen.
Um die Einschränkungen der von einer besitzenden Connection verwalteten DBAPI-Verbindung zu überwinden, ist eine DBAPI-Verbindung auch ohne die Notwendigkeit, zuerst eine Connection zu beschaffen, über die Methode Engine.raw_connection() der Engine verfügbar.
dbapi_conn=engine.raw_connection()
Diese DBAPI-Verbindung ist wieder eine „proxied“-Form, wie es zuvor der Fall war. Der Zweck dieses Proxying wird nun deutlich, denn wenn wir die Methode .close() dieser Verbindung aufrufen, wird die DBAPI-Verbindung typischerweise nicht tatsächlich geschlossen, sondern stattdessen wieder an den Verbindungspool der Engine freigegeben.
dbapi_conn.close()
Während SQLAlchemy in Zukunft möglicherweise eingebaute Muster für weitere DBAPI-Anwendungsfälle hinzufügt, gibt es abnehmende Erträge, da diese Fälle selten benötigt werden und stark vom Typ der verwendeten DBAPI abhängen. In jedem Fall ist das direkte Aufrufen von DBAPI-Mustern für diese Fälle, in denen es benötigt wird, immer vorhanden.
Einige Rezepte für die Verwendung von DBAPI-Verbindungen folgen.
Aufrufen von gespeicherten Prozeduren und benutzerdefinierten Funktionen¶
SQLAlchemy unterstützt das Aufrufen von gespeicherten Prozeduren und benutzerdefinierten Funktionen auf verschiedene Weise. Bitte beachten Sie, dass alle DBAPIs unterschiedliche Praktiken haben, daher müssen Sie die Dokumentation Ihrer zugrunde liegenden DBAPI für spezifische Details in Bezug auf Ihre jeweilige Nutzung konsultieren. Die folgenden Beispiele sind hypothetisch und funktionieren möglicherweise nicht mit Ihrer zugrunde liegenden DBAPI.
Für gespeicherte Prozeduren oder Funktionen mit besonderen syntaktischen oder Parameteranforderungen kann die DBAPI-Level-Methode callproc mit Ihrer DBAPI verwendet werden. Ein Beispiel für dieses Muster ist:
Nicht alle DBAPIs verwenden callproc, und die allgemeinen Nutzungsdetails variieren. Das obige Beispiel ist nur eine Veranschaulichung, wie die Verwendung einer bestimmten DBAPI-Funktion aussehen könnte.
Ihre DBAPI hat möglicherweise keine callproc-Anforderung *oder* erfordert die Ausführung einer gespeicherten Prozedur oder benutzerdefinierten Funktion mit einem anderen Muster, wie z. B. der normalen SQLAlchemy-Verbindungsnutzung. Ein Beispiel für dieses Nutzungsmuster ist, *zum Zeitpunkt der Erstellung dieser Dokumentation*, die Ausführung einer gespeicherten Prozedur in der PostgreSQL-Datenbank mit der psycopg2-DBAPI, die mit normaler Verbindungsnutzung aufgerufen werden sollte.
connection.execute("CALL my_procedure();")
Das obige Beispiel ist hypothetisch. Die zugrunde liegende Datenbank unterstützt „CALL“ oder „SELECT“ in diesen Situationen nicht garantiert, und das Schlüsselwort kann je nachdem, ob die Funktion eine gespeicherte Prozedur oder eine benutzerdefinierte Funktion ist, variieren. Sie sollten Ihre zugrunde liegende DBAPI- und Datenbankdokumentation in diesen Situationen konsultieren, um die richtigen Syntax- und Muster zu ermitteln.
Die Unterstützung für mehrere Ergebnismengen ist von einem rohen DBAPI-Cursor über die Methode nextset verfügbar.
connection=engine.raw_connection()try:cursor_obj=connection.cursor()cursor_obj.execute("select * from table1; select * from table2")results_one=cursor_obj.fetchall()cursor_obj.nextset()results_two=cursor_obj.fetchall()cursor_obj.close()finally:connection.close()
Der Aufruf der Funktion create_engine() sucht den angegebenen Dialekt anhand von setuptools entrypoints. Diese entry points können für Drittanbieter-Dialekte im setup.py-Skript eingerichtet werden. Um beispielsweise einen neuen Dialekt „foodialect://“ zu erstellen, sind die Schritte wie folgt:
Erstellen Sie ein Paket namens foodialect.
Das Paket sollte ein Modul mit der Dialektklasse enthalten, die typischerweise eine Unterklasse von sqlalchemy.engine.default.DefaultDialect ist. In diesem Beispiel sei es FooDialect und sein Modul ist über foodialect.dialect zugänglich.
Der Entrypoint kann in setup.cfg wie folgt eingerichtet werden:
Wenn der Dialekt Unterstützung für eine bestimmte DBAPI auf Basis einer bestehenden, von SQLAlchemy unterstützten Datenbank anbietet, kann der Name einschließlich einer Datenbankqualifizierung angegeben werden. Wenn FooDialect beispielsweise tatsächlich ein MySQL-Dialekt wäre, könnte der Entrypoint wie folgt eingerichtet werden:
SQLAlchemy ermöglicht auch die Registrierung eines Dialekts im aktuellen Prozess, wodurch die Notwendigkeit einer separaten Installation entfällt. Verwenden Sie die Funktion register() wie folgt:
Bietet hochrangige Funktionalität für eine umhüllte DB-API-Verbindung.
Das Objekt Connection wird durch Aufruf der Methode Engine.connect() des Objekts Engine beschafft und bietet Dienste für die Ausführung von SQL-Anweisungen sowie für die Transaktionssteuerung.
Das Connection-Objekt ist **nicht** threadsicher. Während eine Connection zwischen Threads unter Verwendung ordnungsgemäß synchronisierten Zugriffs geteilt werden kann, ist es dennoch möglich, dass die zugrunde liegende DBAPI-Verbindung keinen gemeinsamen Zugriff zwischen Threads unterstützt. Details hierzu finden Sie in der Dokumentation der DBAPI.
Das Connection-Objekt repräsentiert eine einzelne DBAPI-Verbindung, die aus dem Connection-Pool abgerufen wurde. In diesem Zustand hat der Connection-Pool keinen Einfluss auf die Verbindung, einschließlich ihres Ablaufs oder ihres Timeout-Zustands. Damit der Connection-Pool Verbindungen ordnungsgemäß verwalten kann, sollten Verbindungen an den Connection-Pool zurückgegeben werden (d. h. connection.close()), wenn die Verbindung nicht verwendet wird.
Klassensignatur
class sqlalchemy.engine.Connection (sqlalchemy.engine.interfaces.ConnectionEventsTarget, sqlalchemy.inspection.Inspectable)
Das zurückgegebene Objekt ist eine Instanz von RootTransaction. Dieses Objekt repräsentiert den „Scope“ der Transaktion, der entweder durch Aufruf der Methode Transaction.rollback() oder Transaction.commit() abgeschlossen wird; das Objekt fungiert auch als Kontextmanager, wie oben gezeigt.
Die Methode Connection.begin() startet eine Transaktion, die normalerweise in jedem Fall gestartet wird, wenn die Verbindung zum ersten Mal zur Ausführung einer Anweisung verwendet wird. Der Grund für die Verwendung dieser Methode könnte darin bestehen, das Ereignis ConnectionEvents.begin() zu einem bestimmten Zeitpunkt aufzurufen oder Code im Rahmen eines Verbindungs-Checkouts in Bezug auf kontextverwaltete Blöcke zu organisieren, wie z. B.:
Der obige Code unterscheidet sich im Verhalten nicht grundlegend von dem folgenden Code, der Connection.begin() nicht verwendet; der untenstehende Stil wird als „commit-as-you-go“-Stil bezeichnet.
Aus der Datenbankperspektive gibt die Methode Connection.begin() keine SQL-Befehle aus und verändert den Zustand der zugrunde liegenden DBAPI-Verbindung in keiner Weise; die Python DBAPI hat kein Konzept für explizite Transaktionsstarts.
Das zurückgegebene Objekt ist eine Instanz von NestedTransaction, die transaktionale Methoden NestedTransaction.commit() und NestedTransaction.rollback() enthält; für eine verschachtelte Transaktion entsprechen diese Methoden den Operationen „RELEASE SAVEPOINT <name>“ und „ROLLBACK TO SAVEPOINT <name>“. Der Name des Savepoints ist lokal für das Objekt NestedTransaction und wird automatisch generiert. Wie jede andere Transaction kann die NestedTransaction als Kontextmanager verwendet werden, wie oben gezeigt, der entsprechend „released“ oder „rolled back“ wird, je nachdem, ob die Operation innerhalb des Blocks erfolgreich war oder eine Ausnahme ausgelöst hat.
Verschachtelte Transaktionen erfordern SAVEPOINT-Unterstützung in der zugrunde liegenden Datenbank, andernfalls ist das Verhalten undefiniert. SAVEPOINT wird häufig verwendet, um Operationen innerhalb einer Transaktion auszuführen, die fehlschlagen können, während die äußere Transaktion fortgesetzt wird. Z. B.:
fromsqlalchemyimportexcwithengine.begin()asconnection:trans=connection.begin_nested()try:connection.execute(table.insert(),{"username":"sandy"})trans.commit()exceptexc.IntegrityError:# catch for duplicate usernametrans.rollback()# rollback to savepoint# outer transaction continuesconnection.execute(...)
Wenn Connection.begin_nested() aufgerufen wird, ohne zuvor Connection.begin() oder Engine.begin() aufgerufen zu haben, wird das Objekt Connection zuerst die äußere Transaktion „autobeginnen“. Diese äußere Transaktion kann im Stil „commit-as-you-go“ committet werden, z. B.:
withengine.connect()asconnection:# begin() wasn't calledwithconnection.begin_nested():# will auto-"begin()" firstconnection.execute(...)# savepoint is releasedconnection.execute(...)# explicitly commit outer transactionconnection.commit()# can continue working with connection here
Geändert in Version 2.0: Connection.begin_nested() wird nun am „autobegin“-Verhalten der Verbindung teilnehmen, das neu ab 2.0 / „future“-Style-Verbindungen in 1.4 ist.
Dies führt zur Freigabe der zugrunde liegenden Datenbankressourcen, d. h. der intern referenzierten DBAPI-Verbindung. Die DBAPI-Verbindung wird typischerweise an den Verbindungen haltenden Pool zurückgegeben, auf den von der Engine, die diese Connection erzeugt hat, verwiesen wird. Jeder transaktionale Zustand auf der DBAPI-Verbindung wird ebenfalls bedingungslos über die Methode rollback() der DBAPI-Verbindung freigegeben, unabhängig von jedem Transaction-Objekt, das möglicherweise in Bezug auf diese Connection offen ist.
Dies hat zur Folge, dass auch Connection.rollback() aufgerufen wird, falls eine Transaktion im Gange ist.
Nachdem Connection.close() aufgerufen wurde, befindet sich die Connection dauerhaft in einem geschlossenen Zustand und erlaubt keine weiteren Operationen.
Diese Methode committet die aktuelle Transaktion, falls eine gestartet wurde. Wurde keine Transaktion gestartet, hat die Methode keine Auswirkung, vorausgesetzt, die Verbindung befindet sich in einem nicht ungültigen Zustand.
Eine Transaktion wird auf einer Connection automatisch gestartet, sobald eine Anweisung zum ersten Mal ausgeführt wird oder wenn die Methode Connection.begin() aufgerufen wird.
Die von dieser Connection verwaltete zugrunde liegende DB-API-Verbindung.
Dies ist eine von SQLAlchemy-Connection-Pools proxysierte Verbindung, die dann das Attribut _ConnectionFairy.dbapi_connection hat, das auf die eigentliche Treiberverbindung verweist.
Trennt die zugrunde liegende DB-API-Verbindung von ihrem Connection-Pool.
Z. B.
withengine.connect()asconn:conn.detach()conn.execute(text("SET search_path TO schema1, schema2"))# work with connection# connection is fully closed (since we used "with:", can# also call .close())
Diese Instanz von Connection bleibt nutzbar. Wenn sie geschlossen wird (oder aus einem Kontextmanager-Kontext wie oben austritt), wird die DB-API-Verbindung physisch geschlossen und nicht an ihren ursprünglichen Pool zurückgegeben.
Diese Methode kann verwendet werden, um den Rest einer Anwendung von einem geänderten Zustand einer Verbindung (wie z. B. einer Transaktionsisolationsebene oder ähnlichem) zu isolieren.
Führt eine String-SQL-Anweisung direkt auf dem DBAPI-Cursor aus, ohne SQL-Kompilierungsschritte.
Dies kann verwendet werden, um beliebige Strings direkt an die Methode cursor.execute() der verwendeten DBAPI zu übergeben.
Parameter:
statement¶ – Die auszuführende Anweisung als String. Gebundene Parameter müssen den Paramstyle der zugrunde liegenden DBAPI verwenden, wie z. B. „qmark“, „pyformat“, „format“ usw.
parameters¶ – repräsentiert gebundene Parameterwerte, die bei der Ausführung verwendet werden. Das Format ist eines davon: ein Wörterbuch mit benannten Parametern, ein Tupel von positionellen Parametern oder eine Liste, die entweder Wörterbücher oder Tupel für die Unterstützung mehrerer Ausführungen enthält.
parameters¶ – Parameter, die in die Anweisung gebunden werden. Dies kann entweder ein Wörterbuch mit Parameternamen und Werten oder eine mutable Sequenz (z. B. eine Liste) von Wörterbüchern sein. Wenn eine Liste von Wörterbüchern übergeben wird, verwendet die zugrunde liegende Anweisungsausführung die Methode cursor.executemany() der DBAPI. Wenn ein einzelnes Wörterbuch übergeben wird, wird die Methode cursor.execute() der DBAPI verwendet.
execution_options¶ – optionales Wörterbuch von Ausführungsoptionen, das der Anweisungsausführung zugeordnet wird. Dieses Wörterbuch kann eine Teilmenge der Optionen bereitstellen, die von Connection.execution_options() akzeptiert werden.
Setze nicht-SQL-Optionen für die Verbindung, die während der Ausführung wirksam werden.
Diese Methode modifiziert diese Connectionin-place; der Rückgabewert ist dasselbe Connection-Objekt, auf dem die Methode aufgerufen wird. Beachten Sie, dass dies im Gegensatz zum Verhalten der execution_options-Methoden auf anderen Objekten wie Engine.execution_options() und Executable.execution_options() steht. Der Grund dafür ist, dass viele solcher Ausführungsoptionen den Zustand der zugrunde liegenden DBAPI-Verbindung ohnehin modifizieren müssen, so dass es keine praktikable Möglichkeit gibt, die Auswirkung einer solchen Option auf eine "untergeordnete" Verbindung zu lokalisieren.
Geändert in Version 2.0: Die Methode Connection.execution_options() modifiziert im Gegensatz zu anderen Objekten mit dieser Methode die Verbindung in-place, ohne eine Kopie davon zu erstellen.
Die von SQLAlchemy selbst erkannten Schlüsselwörter umfassen alle, die unter Executable.execution_options() aufgeführt sind, sowie weitere, die spezifisch für Connection sind.
Ein Wörterbuch, in dem Compiled-Objekte zwischengespeichert werden, wenn die Connection eine Klauselausdruck in ein Compiled-Objekt kompiliert. Dieses Wörterbuch überschreibt den Statement-Cache, der möglicherweise auf der Engine selbst konfiguriert ist. Wenn es auf None gesetzt ist, wird das Caching deaktiviert, auch wenn die Engine eine konfigurierte Cache-Größe hat.
Beachten Sie, dass die ORM für einige Operationen ihre eigenen "kompilierten" Caches verwendet, einschließlich Flush-Operationen. Das von der ORM intern verwendete Caching überschreibt ein hier angegebenes Cache-Wörterbuch.
Fügt den angegebenen String-Token in Klammern in Protokollnachrichten ein, die von der Verbindung protokolliert werden, d.h. die Protokollierung, die entweder über das Flag create_engine.echo oder über den Logger logging.getLogger("sqlalchemy.engine") aktiviert wird. Dies ermöglicht einen pro-Verbindung oder pro-Sub-Engine-Token, der für das Debugging von parallelen Verbindungsszenarien nützlich ist.
Legt die Transaktionsisolationsstufe für die Lebensdauer dieses Connection-Objekts fest. Gültige Werte sind die Zeichenkettenwerte, die vom Parameter create_engine.isolation_level akzeptiert werden, der an create_engine() übergeben wird. Diese Stufen sind halbdatenbankspezifisch; siehe die Dokumentation des einzelnen Dialekts für gültige Stufen.
Die Isolationsstufenoption wendet die Isolationsstufe an, indem sie Statements auf der DBAPI-Verbindung ausgibt, und **beeinträchtigt notwendigerweise das ursprüngliche Connection-Objekt insgesamt**. Die Isolationsstufe bleibt auf der gegebenen Einstellung, bis sie explizit geändert wird, oder wenn die DBAPI-Verbindung selbst freigegeben wird (d.h. die Methode Connection.close() aufgerufen wird), woraufhin ein Ereignishandler zusätzliche Statements auf der DBAPI-Verbindung ausgibt, um die Isolationsstufenänderung rückgängig zu machen.
Hinweis
Die Ausführungsoption isolation_level kann nur vor dem Aufruf der Methode Connection.begin() sowie vor der Ausgabe von SQL-Statements, die andernfalls "autobegin" auslösen würden, oder direkt nach einem Aufruf von Connection.commit() oder Connection.rollback() festgelegt werden. Eine Datenbank kann die Isolationsstufe einer laufenden Transaktion nicht ändern.
Hinweis
Die Ausführungsoption isolation_level wird implizit zurückgesetzt, wenn die Connection ungültig wird, z. B. über die Methode Connection.invalidate(), oder wenn ein Trennungsfehler auftritt. Die nach der Ungültigkeitserklärung erzeugte neue Verbindung hat die ausgewählte Isolationsstufe nicht automatisch wieder angewendet.
Wenn True, wird bei einer vollständig leeren Parameterliste oder einem leeren Wörterbuch die Anweisung auf dem Cursor als cursor.execute(statement) aufgerufen, ohne die Parameter-Sammlung überhaupt zu übergeben. Einige DBAPIs wie psycopg2 und mysql-python betrachten Prozentzeichen nur dann als signifikant, wenn Parameter vorhanden sind; diese Option ermöglicht es Code, SQL mit Prozentzeichen (und möglicherweise anderen Zeichen) zu generieren, das neutral ist, ob es vom DBAPI ausgeführt oder in ein Skript geleitet wird, das später von Kommandozeilenwerkzeugen aufgerufen wird.
Weist den Dialekt an, die Ergebnisse nach Möglichkeit zu "streamen" und nicht vorab zu puffern. Für Backends wie PostgreSQL, MySQL und MariaDB weist dies die Verwendung eines "serverseitigen Cursors" im Gegensatz zu einem clientseitigen Cursor an. Andere Backends wie die von Oracle Database verwenden möglicherweise bereits standardmäßig serverseitige Cursor.
Die Verwendung von Connection.execution_options.stream_results wird normalerweise mit der Festlegung einer festen Anzahl von Zeilen kombiniert, die in Stapeln abgerufen werden sollen, um eine effiziente Iteration von Datenbankzeilen zu ermöglichen, ohne dabei alle Ergebniszeilen auf einmal in den Speicher zu laden. Dies kann auf einem Result-Objekt mit der Methode Result.yield_per() konfiguriert werden, nachdem die Ausführung ein neues Result zurückgegeben hat. Wenn Result.yield_per() nicht verwendet wird, verwendet der Connection.execution_options.stream_results-Betriebsmodus stattdessen einen dynamisch dimensionierten Puffer, der Zeilensätze pro Durchgang puffert, sich bei jedem Stapel basierend auf einer festen Wachstumsgröße erhöht, bis zu einem Limit, das mit dem Parameter Connection.execution_options.max_row_buffer konfiguriert werden kann.
Verfügbar für: Connection, Executable. Legt eine maximale Puffergröße fest, die verwendet wird, wenn die Ausführungsoption Connection.execution_options.stream_results auf einem Backend verwendet wird, das serverseitige Cursor unterstützt. Der Standardwert, falls nicht angegeben, ist 1000.
Verfügbar für: Connection, Engine. Anzahl der Zeilen, die in eine INSERT-Anweisung formatiert werden, wenn die Anweisung den "insertmanyvalues"-Modus verwendet, der eine paginierte Form von Masseneinfügungen ist und für viele Backends bei Verwendung von executemany typischerweise in Verbindung mit RETURNING verwendet wird. Standardmäßig 1000. Kann auch auf Engine-Basis über den Parameter create_engine.insertmanyvalues_page_size modifiziert werden.
Ein Wörterbuch, das Schemanamen auf Schemanamen abbildet und auf das schema-Element jeder Table angewendet wird, wenn SQL- oder DDL-Ausdruckselemente in Zeichenketten kompiliert werden; der resultierende Schemaname wird basierend auf der Anwesenheit des ursprünglichen Namens in der Zuordnung konvertiert.
Boolean; wenn True, wird das Attribut cursor.rowcount bedingungslos im Ergebnis memoisiert und über das Attribut CursorResult.rowcount zugänglich gemacht. Normalerweise wird dieses Attribut nur für UPDATE- und DELETE-Statements beibehalten. Mit dieser Option kann der rowcount-Wert des DBAPI für andere Arten von Statements wie INSERT und SELECT abgerufen werden, soweit der DBAPI diese Statements unterstützt. Siehe CursorResult.rowcount für Hinweise zum Verhalten dieses Attributs.
Gibt die aktuelle **tatsächliche** Isolationsstufe zurück, die auf der Datenbank im Geltungsbereich dieser Verbindung vorhanden ist.
Dieses Attribut führt eine Live-SQL-Operation gegen die Datenbank aus, um die aktuelle Isolationsstufe zu ermitteln. Daher ist der zurückgegebene Wert die tatsächliche Stufe auf der zugrunde liegenden DBAPI-Verbindung, unabhängig davon, wie dieser Zustand festgelegt wurde. Dies wird eine der vier tatsächlichen Isolationsmodi sein: READUNCOMMITTED, READCOMMITTED, REPEATABLEREAD, SERIALIZABLE. Es wird die Isolationsstufen-Einstellung AUTOCOMMIT **nicht** enthalten. Drittanbieter-Dialekte können auch zusätzliche Isolationsstufen-Einstellungen aufweisen.
Hinweis
Diese Methode meldet die Isolationsstufe AUTOCOMMIT **nicht**, da dies eine separate DBAPI-Einstellung ist, die unabhängig von der **tatsächlichen** Isolationsstufe ist. Wenn AUTOCOMMIT verwendet wird, hat die Datenbankverbindung immer noch einen "traditionellen" Isolationsmodus, der typischerweise einer der vier Werte READUNCOMMITTED, READCOMMITTED, REPEATABLEREAD, SERIALIZABLE ist.
Vergleichen Sie dies mit dem Accessor Connection.default_isolation_level, der die Isolationsstufe zurückgibt, die zum Zeitpunkt der erstmaligen Verbindung auf der Datenbank vorhanden war.
Info-Wörterbuch, das der zugrunde liegenden DBAPI-Verbindung zugeordnet ist, auf die sich diese Connection bezieht, und das die Zuordnung benutzerdefinierter Daten zur Verbindung ermöglicht.
Die hier gespeicherten Daten werden zusammen mit der DBAPI-Verbindung übernommen, auch nachdem sie an den Connection-Pool zurückgegeben und in nachfolgenden Instanzen von Connection wiederverwendet werden.
Ungültig machen der zugrunde liegenden DBAPI-Verbindung, die dieser Connection zugeordnet ist.
Es wird versucht, die zugrunde liegende DBAPI-Verbindung sofort zu schließen. Wenn dieser Vorgang fehlschlägt, wird der Fehler protokolliert, aber nicht ausgelöst. Die Verbindung wird dann verworfen, unabhängig davon, ob close() erfolgreich war oder nicht.
Bei der nächsten Verwendung (wobei "Verwendung" typischerweise die Methode Connection.execute() oder ähnliches bedeutet) versucht diese Connection, eine neue DBAPI-Verbindung über die Dienste des Pool als Konnektivitätsquelle zu beschaffen (z. B. eine "Wiederverbindung").
Wenn während der Ausführung der Methode Connection.invalidate() eine Transaktion lief (z. B. die Methode Connection.begin() wurde aufgerufen), gehen auf DBAPI-Ebene alle mit dieser Transaktion verbundenen Zustände verloren, da die DBAPI-Verbindung geschlossen wird. Die Connection erlaubt keine Wiederverbindung, bis das Transaction-Objekt durch Aufruf der Methode Transaction.rollback() beendet wird; bis dahin löst jeder Versuch, die Connection weiter zu verwenden, eine InvalidRequestError aus. Dies soll verhindern, dass Anwendungen versehentlich fortlaufende Transaktionsoperationen fortsetzen, obwohl die Transaktion aufgrund einer Ungültigkeitserklärung verloren gegangen ist.
Macht die aktuell laufende Transaktion rückgängig.
Diese Methode macht die aktuelle Transaktion rückgängig, wenn eine gestartet wurde. Wenn keine Transaktion gestartet wurde, hat die Methode keine Auswirkung. Wenn eine Transaktion gestartet wurde und die Verbindung in einem invalidierten Zustand ist, wird die Transaktion mit dieser Methode gelöscht.
Eine Transaktion wird auf einer Connection automatisch gestartet, sobald eine Anweisung zum ersten Mal ausgeführt wird oder wenn die Methode Connection.begin() aufgerufen wird.
Führt eine SQL-Anweisung aus und gibt ein Skalarobjekt zurück.
Diese Methode ist eine Kurzform für den Aufruf der Methode Result.scalar() nach dem Aufruf der Methode Connection.execute(). Die Parameter sind äquivalent.
Gibt zurück:
ein Skalarer Python-Wert, der die erste Spalte der ersten zurückgegebenen Zeile darstellt.
Eine Reihe von Hooks, die dazu bestimmt sind, die Konstruktion eines Engine-Objekts basierend auf Entrypoint-Namen in einer URL zu erweitern.
Der Zweck von CreateEnginePlugin besteht darin, Drittsystemen zu ermöglichen, Event-Listener auf Engine-, Pool- und Dialektebene anzuwenden, ohne dass die Zielanwendung geändert werden muss. Stattdessen können die Plugin-Namen in der Datenbank-URL hinzugefügt werden. Zu den Zielanwendungen für CreateEnginePlugin gehören
Verbindungs- und SQL-Performance-Tools, z. B. die Events verwenden, um die Anzahl der Ausleihen und/oder die für Anweisungen aufgewendete Zeit zu verfolgen.
Konnektivitäts-Plugins wie Proxys.
Ein rudimentäres CreateEnginePlugin, das einen Logger an ein Engine-Objekt anhängt, könnte wie folgt aussehen:
importloggingfromsqlalchemy.engineimportCreateEnginePluginfromsqlalchemyimporteventclassLogCursorEventsPlugin(CreateEnginePlugin):def__init__(self,url,kwargs):# consume the parameter "log_cursor_logging_name" from the# URL querylogging_name=url.query.get("log_cursor_logging_name","log_cursor")self.log=logging.getLogger(logging_name)defupdate_url(self,url):"update the URL to one that no longer includes our parameters"returnurl.difference_update_query(["log_cursor_logging_name"])defengine_created(self,engine):"attach an event listener after the new Engine is constructed"event.listen(engine,"before_cursor_execute",self._log_event)def_log_event(self,conn,cursor,statement,parameters,context,executemany,):self.log.info("Plugin logged cursor event: %s",statement)
Plugins werden über Entry Points registriert, ähnlich wie Dialekte.
Der URL-Parameter plugin unterstützt mehrere Instanzen, sodass eine URL mehrere Plugins angeben kann; sie werden in der Reihenfolge geladen, in der sie in der URL aufgeführt sind.
Neu in Version 1.2.3: Plugin-Namen können auch als Liste an create_engine() übergeben werden.
Ein Plugin kann Plugin-spezifische Argumente aus dem URL-Objekt sowie aus dem kwargs-Dictionary verbrauchen, welches das Dictionary von Argumenten ist, die an den Aufruf von create_engine() übergeben werden. Das "Verbrauchen" dieser Argumente bedeutet, dass sie bei der Initialisierung des Plugins entfernt werden müssen, damit die Argumente nicht an den Dialect-Konstruktor übergeben werden, wo sie zu einem ArgumentError führen würden, da sie vom Dialekt nicht erkannt werden.
Ab Version 1.4 von SQLAlchemy sollten Argumente weiterhin direkt aus dem kwargs-Dictionary verbraucht werden, indem die Werte mit einer Methode wie dict.pop entfernt werden. Argumente aus dem URL-Objekt sollten durch Implementierung der Methode CreateEnginePlugin.update_url() verbraucht werden, die eine neue Kopie des URL mit entfernten Plugin-spezifischen Parametern zurückgibt.
Geändert in Version 1.4: Das URL-Objekt ist jetzt unveränderlich; ein CreateEnginePlugin, das die URL ändern muss, sollte die neu hinzugefügte Methode CreateEnginePlugin.update_url() implementieren, die nach der Konstruktion des Plugins aufgerufen wird.
Zur Migration konstruieren Sie das Plugin auf folgende Weise und prüfen auf die Existenz der Methode CreateEnginePlugin.update_url(), um die laufende Version zu erkennen.
classMyPlugin(CreateEnginePlugin):def__init__(self,url,kwargs):ifhasattr(CreateEnginePlugin,"update_url"):# detect the 1.4 APIself.my_argument_one=url.query["my_argument_one"]self.my_argument_two=url.query["my_argument_two"]else:# detect the 1.3 and earlier API - mutate the# URL directlyself.my_argument_one=url.query.pop("my_argument_one")self.my_argument_two=url.query.pop("my_argument_two")self.my_argument_three=kwargs.pop("my_argument_three",None)defupdate_url(self,url):# this method is only called in the 1.4 versionreturnurl.difference_update_query(["my_argument_one","my_argument_two"])
Wenn der Engine-Erstellungsprozess abgeschlossen ist und das Engine-Objekt erstellt wurde, wird es über den Hook CreateEnginePlugin.engine_created() erneut an das Plugin übergeben. In diesem Hook können weitere Änderungen am Engine vorgenommen werden, meist im Zusammenhang mit der Einrichtung von Events (z. B. die in Core Events definierten).
das URL-Objekt. Das Plugin kann die URL nach Argumenten durchsuchen. Vom Plugin verwendete Argumente sollten entfernt werden, indem eine aktualisierte URL von der Methode CreateEnginePlugin.update_url() zurückgegeben wird.
Eine neue URL sollte zurückgegeben werden. Diese Methode wird typischerweise verwendet, um Konfigurationsargumente aus der URL zu verbrauchen, die entfernt werden müssen, da sie vom Dialekt nicht erkannt werden. Die Methode URL.difference_update_query() steht zur Verfügung, um diese Argumente zu entfernen. Siehe die Docstring bei CreateEnginePlugin für ein Beispiel.
Löscht den kompilierten Cache, der mit dem Dialekt verbunden ist.
Dies gilt **nur** für den integrierten Cache, der über den Parameter create_engine.query_cache_size eingerichtet wird. Er beeinflusst keine Dictionary-Caches, die über den Parameter Connection.execution_options.compiled_cache übergeben wurden.
Die Connection fungiert als Python-Kontextmanager, sodass die typische Verwendung dieser Methode wie folgt aussieht:
withengine.connect()asconnection:connection.execute(text("insert into table values ('foo')"))connection.commit()
Wo oben, nach Abschluss des Blocks, die Verbindung "geschlossen" wird und ihre zugrunde liegenden DBAPI-Ressourcen an den Connection-Pool zurückgegeben werden. Dies hat auch zur Folge, dass jede Transaktion zurückgerollt wird, die explizit begonnen wurde oder über autobegin begonnen wurde, und das Ereignis ConnectionEvents.rollback() ausgelöst wird, falls eine gestartet wurde und noch aktiv ist.
Entsorgt den von diesem Engine verwendeten Connection-Pool.
Ein neuer Connection-Pool wird unmittelbar nach der Entsorgung des alten erstellt. Der vorherige Connection-Pool wird entweder aktiv entsorgt, indem alle aktuell eingecheckten Verbindungen in diesem Pool geschlossen werden, oder passiv, indem Referenzen darauf verloren gehen, aber ansonsten keine Verbindungen geschlossen werden. Letztere Strategie ist für einen Initialisierer in einem geforkten Python-Prozess besser geeignet.
Wenn auf dem Standardwert True belassen, werden alle derzeit ausgecheckten Datenbankverbindungen vollständig geschlossen. Verbindungen, die noch ausgecheckt sind, werden nicht geschlossen, aber sie werden nicht mehr mit dieser Engine verknüpft sein. Wenn sie also einzeln geschlossen werden, wird der Pool, mit dem sie verknüpft sind, schließlich vom Garbage Collector aufgeräumt und sie werden vollständig geschlossen, falls sie nicht bereits beim Auschecken geschlossen wurden.
Wenn auf False gesetzt, wird der vorherige Verbindungs-Pool dereferenziert und ansonsten in keiner Weise berührt.
Neu in Version 1.4.33: Der Parameter Engine.dispose.close wurde hinzugefügt, um den Ersatz eines Verbindungs-Pools in einem Kindprozess zu ermöglichen, ohne die vom Elternprozess verwendeten Verbindungen zu beeinträchtigen.
Gibt eine neue Engine zurück, die Connection Objekte mit den angegebenen Ausführungsoptionen bereitstellt.
Die zurückgegebene Engine bleibt mit der ursprünglichen Engine verwandt, da sie denselben Verbindungs-Pool und andere Zustände teilt.
Der von der neuen Engine verwendete Pool ist dieselbe Instanz. Die Methode Engine.dispose() ersetzt die Verbindungs-Pool-Instanz sowohl für die übergeordnete als auch für diese Engine.
Event-Listener sind „kaskadiert“ – das bedeutet, die neue Engine erbt die Events der übergeordneten Engine, und neue Events können individuell der neuen Engine zugeordnet werden.
Die Protokollierungskonfiguration und der logging_name werden von der übergeordneten Engine kopiert.
Die Absicht der Methode Engine.execution_options() ist die Implementierung von Schemata, bei denen mehrere Engine-Objekte auf denselben Verbindungs-Pool verweisen, die sich jedoch durch Optionen unterscheiden, die das Ausführungsverhalten für jede Engine beeinflussen. Ein solches Beispiel ist die Aufteilung in separate „Reader“- und „Writer“-Engine-Instanzen, wobei eine Engine eine niedrigere Isolationsstufe konfiguriert hat oder sogar transaktionsunfähig ist, indem „autocommit“ verwendet wird. Ein Beispiel für diese Konfiguration finden Sie unter Beibehaltung mehrerer Isolationsstufen für eine einzelne Engine.
Ein weiteres Beispiel ist eines, das eine benutzerdefinierte Option shard_id verwendet, die von einem Event zur Änderung des aktuellen Schemas einer Datenbankverbindung konsumiert wird.
Das obige Rezept veranschaulicht zwei Engine-Objekte, die jeweils als Fabriken für Connection-Objekte dienen, die vordefinierte „shard_id“-Ausführungsoptionen aufweisen. Ein ConnectionEvents.before_cursor_execute() Event-Handler interpretiert dann diese Ausführungsoption, um eine MySQL use-Anweisung auszugeben, um die Datenbank vor der Ausführung einer Anweisung zu wechseln, während gleichzeitig verfolgt wird, welche Datenbank wir mithilfe des Connection.info-Dictionarys eingerichtet haben.
Gibt eine „rohe“ DBAPI-Verbindung aus dem Verbindungs-Pool zurück.
Das zurückgegebene Objekt ist eine proxy-gesteuerte Version des DBAPI-Verbindungsobjekts, das vom zugrunde liegenden Treiber verwendet wird. Das Objekt hat das gleiche Verhalten wie die echte DBAPI-Verbindung, mit der Ausnahme, dass seine close()-Methode dazu führt, dass die Verbindung an den Pool zurückgegeben wird, anstatt tatsächlich geschlossen zu werden.
Diese Methode bietet direkten DBAPI-Verbindungszugriff für besondere Situationen, in denen die von Connection bereitgestellte API nicht benötigt wird. Wenn ein Connection-Objekt bereits vorhanden ist, ist die DBAPI-Verbindung über den Connection.connection-Accessor verfügbar.
Aktualisiert das Standard-Dictionary `execution_options` dieser Engine.
Die angegebenen Schlüssel/Werte in **opt werden zu den Standard-Ausführungsoptionen hinzugefügt, die für alle Verbindungen verwendet werden. Der anfängliche Inhalt dieses Dictionaries kann über den Parameter execution_options an create_engine() übergeben werden.
Dieses Objekt existiert ausschließlich, um an das DialectEvents.handle_error() Event übergeben zu werden und unterstützt eine Schnittstelle, die ohne Rückwärtskompatibilität erweitert werden kann.
Der ExecutionContext, der dem gerade stattfindenden Ausführungsaufruf entspricht.
Dies ist bei Anweisungsausführungsoperationen vorhanden, jedoch nicht bei Operationen wie dem Starten/Beenden von Transaktionen. Es ist auch nicht vorhanden, wenn die Ausnahme ausgelöst wurde, bevor die ExecutionContext erstellt werden konnte.
Stellt dar, ob alle Verbindungen im Pool ungültig gemacht werden sollen, wenn eine „Verbindungsabbruch“-Bedingung vorliegt.
Wenn dieses Flag innerhalb des Geltungsbereichs des DialectEvents.handle_error() Events auf False gesetzt wird, hat dies zur Folge, dass die gesamte Sammlung von Verbindungen im Pool bei einem Verbindungsabbruch nicht ungültig gemacht wird; nur die aktuelle Verbindung, die Gegenstand des Fehlers ist, wird tatsächlich ungültig gemacht.
Der Zweck dieses Flags sind benutzerdefinierte Behandlungsroutinen für Verbindungsabbrüche, bei denen die Ungültigmachung anderer Verbindungen im Pool basierend auf anderen Bedingungen oder sogar pro Verbindung durchgeführt werden soll.
SQLAlchemy berücksichtigt dieses Flag, um zu entscheiden, ob die Verbindung anschließend ungültig gemacht werden soll oder nicht. Das heißt, durch Zuweisung zu diesem Flag kann ein „Verbindungsabbruch“-Ereignis, das dann zu einer Verbindungs- und Pool-Ungültigmachung führt, aufgerufen oder verhindert werden, indem dieses Flag geändert wird.
Hinweis
Der Pool-„pre_ping“-Handler, der mit dem Parameter create_engine.pool_pre_ping aktiviert wird, prüft nicht dieses Event, bevor entschieden wird, ob das „ping“ falsch zurückgegeben hat, im Gegensatz zum Empfang eines unbehandelten Fehlers. Für diesen Anwendungsfall kann das ältere Rezept basierend auf engine_connect() verwendet werden. Eine zukünftige API wird eine umfassendere Anpassung des Mechanismus zur Erkennung von Verbindungsabbrüchen über alle Funktionen hinweg ermöglichen.
Die sqlalchemy.exc.StatementError, die die ursprüngliche umschließt und ausgelöst wird, wenn die Ausnahmsbehandlung durch das Event umgangen wird.
Kann None sein, da nicht alle Ausnahmetypen von SQLAlchemy umschlossen werden. Für Ausnahmen auf DBAPI-Ebene, die von der `Error`-Klasse der DBAPI abgeleitet sind, ist dieses Feld immer vorhanden.
Bei Verwendung von NestedTransaction sind die Semantiken von „begin“ / „commit“ / „rollback“ wie folgt:
Die „begin“-Operation entspricht dem Befehl „BEGIN SAVEPOINT“, wobei dem Savepoint ein expliziter Name gegeben wird, der Teil des Zustands dieses Objekts ist.
Der Grund für die Nachahmung der Semantik einer äußeren Transaktion in Bezug auf Savepoints ist, dass Code eine „Savepoint“-Transaktion und eine „äußere“ Transaktion auf eine agnostische Weise behandeln kann.
Wenn diese Transaktion die Basis-Transaktion in einer verschachtelten begin/commit-Struktur ist, wird die Transaktion rollback() aufrufen. Andernfalls gibt die Methode zurück.
Dies wird verwendet, um eine Transaktion abzubrechen, ohne den Geltungsbereich einer umschließenden Transaktion zu beeinträchtigen.
Im 2.0-Stil verwendet die Connection auch das „Autobegin“-Verhalten, das eine neue RootTransaction erstellt, wenn eine Verbindung in einem nicht-transaktionalen Zustand verwendet wird, um Befehle auf der DBAPI-Verbindung auszugeben. Der Geltungsbereich der RootTransaction im 2.0-Stil kann mit den Methoden Connection.commit() und Connection.rollback() gesteuert werden.
Wenn diese Transaktion die Basis-Transaktion in einer verschachtelten begin/commit-Struktur ist, wird die Transaktion rollback() aufrufen. Andernfalls gibt die Methode zurück.
Dies wird verwendet, um eine Transaktion abzubrechen, ohne den Geltungsbereich einer umschließenden Transaktion zu beeinträchtigen.
fromsqlalchemyimportcreate_engineengine=create_engine("postgresql+psycopg2://scott:tiger@localhost/test")connection=engine.connect()trans=connection.begin()connection.execute(text("insert into x (a, b) values (1, 2)"))trans.commit()
Das Objekt bietet die Methoden rollback() und commit() zur Steuerung von Transaktionsgrenzen. Es implementiert auch eine Context-Manager-Schnittstelle, so dass die Python with-Anweisung mit der Methode Connection.begin() verwendet werden kann.
withconnection.begin():connection.execute(text("insert into x (a, b) values (1, 2)"))
Das Transaktionsobjekt ist **nicht** threadsicher.
Wenn diese Transaktion die Basis-Transaktion in einer verschachtelten begin/commit-Struktur ist, wird die Transaktion rollback() aufrufen. Andernfalls gibt die Methode zurück.
Dies wird verwendet, um eine Transaktion abzubrechen, ohne den Geltungsbereich einer umschließenden Transaktion zu beeinträchtigen.
Wenn diese Transaktion die Basis-Transaktion in einer verschachtelten begin/commit-Struktur ist, wird die Transaktion rollback() aufrufen. Andernfalls gibt die Methode zurück.
Dies wird verwendet, um eine Transaktion abzubrechen, ohne den Geltungsbereich einer umschließenden Transaktion zu beeinträchtigen.
Ein IteratorResult, der aus einem auf einem Iterator basierenden aufrufbaren Objekt arbeitet.
Das angegebene Argument chunks ist eine Funktion, die eine Anzahl von Zeilen erhält, die in jedem Chunk zurückgegeben werden sollen, oder None für alle Zeilen. Die Funktion sollte dann einen nicht verbrauchten Iterator von Listen zurückgeben, wobei jede Liste die angegebene Größe hat.
Die Funktion kann jederzeit erneut aufgerufen werden, wobei sie vom selben Ergebnisdatensatz fortfahren und die Chunk-Größe wie angegeben anpassen sollte.
Konfiguriert die Strategie zum Abrufen von Zeilen, um num Zeilen auf einmal abzurufen.
Dies beeinflusst das zugrunde liegende Verhalten des Ergebnisses, wenn über das Ergebnisobjekt iteriert wird oder Methoden wie Result.fetchone() verwendet werden, die jeweils eine Zeile zurückgeben. Daten aus dem zugrunde liegenden Cursor oder einer anderen Datenquelle werden bis zu dieser Anzahl von Zeilen im Speicher gepuffert, und die gepufferte Sammlung wird dann zeilenweise oder in der angeforderten Anzahl von Zeilen ausgegeben. Jedes Mal, wenn der Puffer geleert wird, wird er mit dieser Anzahl von Zeilen aufgefüllt oder mit weniger Zeilen, wenn weniger vorhanden sind.
Die Methode Result.yield_per() wird im Allgemeinen in Verbindung mit der Ausführungsoption Connection.execution_options.stream_results verwendet, die es dem verwendeten Datenbankdialekt ermöglicht, einen serverseitigen Cursor zu nutzen, falls die DBAPI einen spezifischen „serverseitigen Cursor“-Modus unterstützt, der sich von seinem Standardbetrieb unterscheidet.
num¶ – Anzahl der Zeilen, die jedes Mal abgerufen werden sollen, wenn der Puffer neu gefüllt wird. Wenn auf einen Wert kleiner als 1 gesetzt, werden alle Zeilen für den nächsten Puffer abgerufen.
Ein Ergebnis, das den Zustand eines DBAPI-Cursors repräsentiert.
Geändert in Version 1.4: Die Klasse CursorResult` ersetzt die vorherige ResultProxy-Schnittstelle. Diese Klassen basieren auf der Aufruf-API von Result, die ein aktualisiertes Nutzungsmodell und eine aktualisierte Aufruffassade für SQLAlchemy Core und SQLAlchemy ORM bietet.
Gibt Datenbankzeilen über die Klasse Row zurück, die zusätzliche API-Funktionen und Verhaltensweisen auf den von der DBAPI zurückgegebenen Rohdaten bietet. Durch die Verwendung von Filtern wie der Methode Result.scalars() können auch andere Arten von Objekten zurückgegeben werden.
Dies schließt den zugrunde liegenden DBAPI-Cursor, der der Anweisungsausführung entspricht, falls noch einer vorhanden ist. Beachten Sie, dass der DBAPI-Cursor automatisch freigegeben wird, wenn das CursorResult alle verfügbaren Zeilen erschöpft hat. CursorResult.close() ist im Allgemeinen eine optionale Methode, außer im Fall des Verwerfens eines CursorResult, das noch zusätzliche Zeilen zum Abrufen ausstehend hat.
Nachdem diese Methode aufgerufen wurde, ist es nicht mehr gültig, die Abrufmethoden aufzurufen, was bei nachfolgender Verwendung eine ResourceClosedError auslöst.
Definieren Sie die Spalten, die in jeder Zeile zurückgegeben werden sollen.
Diese Methode kann verwendet werden, um die zurückgegebenen Spalten zu begrenzen und sie neu zu ordnen. Die angegebenen Ausdrücke sind normalerweise eine Reihe von Ganzzahlen oder Zeichenketten-Schlüsselnamen. Sie können auch geeignete ColumnElement-Objekte sein, die einem gegebenen Anweisungskonstrukt entsprechen.
Geändert in Version 2.0: Aufgrund eines Fehlers in 1.4 verhielt sich die Methode Result.columns() fehlerhaft, da der Aufruf der Methode mit nur einem Index dazu führte, dass das Result-Objekt Skalarwerte anstelle von Row-Objekten lieferte. In Version 2.0 wurde dieses Verhalten korrigiert, sodass der Aufruf von Result.columns() mit einem einzelnen Index ein Result-Objekt erzeugt, das weiterhin Row-Objekte liefert, die nur eine einzelne Spalte enthalten.
*col_expressions¶ – gibt die zurückzugebenden Spalten an. Elemente können Ganzzahl-Zeilenindizes, Zeichenketten-Spaltennamen oder geeignete ColumnElement-Objekte sein, die einem Select-Konstrukt entsprechen.
Gibt zurück:
dieses Result-Objekt mit den gegebenen Modifikationen.
Wenn alle Zeilen erschöpft sind, wird None zurückgegeben.
Diese Methode wird zur Abwärtskompatibilität mit SQLAlchemy 1.x.x bereitgestellt.
Um nur die erste Zeile eines Ergebnisses abzurufen, verwenden Sie die Methode Result.first(). Um durch alle Zeilen zu iterieren, iterieren Sie direkt über das Result-Objekt.
Gibt zurück:
ein Row-Objekt, wenn keine Filter angewendet werden, oder None, wenn keine Zeilen mehr vorhanden sind.
Die erste Zeile abrufen oder None, wenn keine Zeile vorhanden ist.
Schließt den Ergebnissatz und verwirft die restlichen Zeilen.
Hinweis
Diese Methode gibt standardmäßig eine Zeile, z. B. ein Tupel, zurück. Um genau einen einzelnen Skalarwert, d. h. die erste Spalte der ersten Zeile, zurückzugeben, verwenden Sie die Methode Result.scalar() oder kombinieren Sie Result.scalars() und Result.first().
Zusätzlich, im Gegensatz zum Verhalten der Legacy-ORM-Methode Query.first(), wird **keine Begrenzung** auf die SQL-Abfrage angewendet, die zur Erzeugung dieses Result aufgerufen wurde; für einen DBAPI-Treiber, der Ergebnisse im Speicher puffert, bevor Zeilen ausgegeben werden, werden alle Zeilen an den Python-Prozess gesendet und alle außer der ersten Zeile werden verworfen.
Gibt ein aufrufbares Objekt zurück, das Kopien dieses Result erzeugt, wenn es aufgerufen wird.
Das zurückgegebene aufrufbare Objekt ist eine Instanz von FrozenResult.
Dies wird für das Caching von Ergebnismengen verwendet. Die Methode muss aufgerufen werden, wenn das Ergebnis noch nicht konsumiert wurde, und der Aufruf der Methode verbraucht das Ergebnis vollständig. Wenn die FrozenResult aus einem Cache abgerufen wird, kann sie beliebig oft aufgerufen werden, wobei jedes Mal ein neues Result-Objekt gegen den gespeicherten Zeilensatz erzeugt wird.
Siehe auch
Anweisungen neu ausführen - Beispiel für die Verwendung in der ORM zur Implementierung eines Ergebnis-Caching.
Gibt den Primärschlüssel für die gerade eingefügte Zeile zurück.
Der Rückgabewert ist ein Row-Objekt, das ein benanntes Tupel von Primärschlüsselwerten in der Reihenfolge darstellt, in der die Primärschlüsselspalten in der Quell- Table konfiguriert sind.
Geändert in Version 1.4.8: - der Wert CursorResult.inserted_primary_key ist jetzt ein benanntes Tupel über die Klasse Row, anstatt eines einfachen Tupels.
Dieser Accessor gilt nur für einzelne Zeilen- insert()-Konstrukte, die keine explizit angegebene Insert.returning() hatten. Unterstützung für Mehrzeilen-Inserts, obwohl noch nicht für die meisten Backends verfügbar, wird über den CursorResult.inserted_primary_key_rows Accessor zugegriffen.
Beachten Sie, dass Primärschlüsselspalten, die eine server_default-Klausel angeben oder anderweitig nicht als „autoincrement“-Spalten qualifizieren (siehe Hinweise bei Column) und die mit dem datenbankseitigen Standard generiert wurden, in dieser Liste als None erscheinen, es sei denn, das Backend unterstützt „returning“ und die mit „implicit returning“ aktivierte Anweisung wurde ausgeführt.
Löst InvalidRequestError aus, wenn die ausgeführte Anweisung kein kompiliertes Ausdruckskonstrukt ist oder kein insert()-Konstrukt ist.
Gibt den Wert von CursorResult.inserted_primary_key als Zeile in einer Liste zurück; einige Dialekte können auch eine Mehrzeilenform unterstützen.
Hinweis
Wie unten angegeben, sind diese Accessoren in aktuellen SQLAlchemy-Versionen nur über das hinaus nützlich, was CursorResult.inserted_primary_key bietet, wenn der psycopg2-Dialekt verwendet wird. Zukünftige Versionen hoffen, diese Funktion auf weitere Dialekte zu verallgemeinern.
Dieser Accessor wurde zur Unterstützung von Dialekten hinzugefügt, die die Funktion bieten, die derzeit von der Funktion Psycopg2 Fast Execution Helpers implementiert wird, derzeit **nur der psycopg2-Dialekt**, der es ermöglicht, viele Zeilen auf einmal einzufügen und gleichzeitig das Verhalten beizubehalten, servergenerierte Primärschlüsselwerte zurückgeben zu können.
Bei Verwendung des psycopg2-Dialekts oder anderer Dialekte, die in zukünftigen Versionen „fast executemany“-Stil-Inserts unterstützen könnten: Beim Aufrufen einer INSERT-Anweisung mit einer Liste von Zeilen als zweites Argument zu Connection.execute() liefert dieser Accessor eine Liste von Zeilen, wobei jede Zeile den Primärschlüsselwert für jede eingefügte Zeile enthält.
Bei Verwendung aller anderen Dialekte / Backends, die diese Funktion noch nicht unterstützen: Dieser Accessor ist nur für **Einzelzeilen-INSERT-Anweisungen** nützlich und gibt die gleichen Informationen wie CursorResult.inserted_primary_key in einer Liste mit einem Element zurück. Wenn eine INSERT-Anweisung in Verbindung mit einer Liste von einzufügenden Zeilen ausgeführt wird, enthält die Liste eine Zeile pro in der Anweisung eingefügter Zeile, enthält jedoch None für alle servergenerierten Werte.
Zukünftige Versionen von SQLAlchemy werden die „fast execution helper“-Funktion von psycopg2 für andere Dialekte weiter verallgemeinern, wodurch dieser Accessor allgemeiner nutzbar wird.
True, wenn dieses CursorResult das Ergebnis der Ausführung eines von der Ausdruckssprache kompilierten insert()-Konstrukts ist.
Wenn True, impliziert dies, dass das Attribut inserted_primary_key zugänglich ist, vorausgesetzt, die Anweisung enthielt kein benutzerdefiniertes „returning“-Konstrukt.
geerbt von dersqlalchemy.engine._WithKeys.keysMethode vonsqlalchemy.engine._WithKeys
Gibt eine iterierbare Ansicht zurück, die die Zeichenketten-Schlüssel liefert, die von jedem Row dargestellt würden.
Die Schlüssel können die Bezeichnungen der von einer Core-Anweisung zurückgegebenen Spalten oder die Namen der von einer ORM-Ausführung zurückgegebenen ORM-Klassen darstellen.
Die Ansicht kann auch mithilfe des Python-Operators in auf Schlüsselcontainment getestet werden, wobei sowohl die Zeichenketten-Schlüssel, die in der Ansicht dargestellt sind, als auch alternative Schlüssel wie Spaltenobjekte getestet werden.
Geändert in Version 1.4: Es wird ein Key-View-Objekt zurückgegeben, anstatt einer einfachen Liste.
Gibt den ‚lastrowid‘-Accessor auf dem DBAPI-Cursor zurück.
Dies ist eine DBAPI-spezifische Methode und nur für die Backends funktional, die sie unterstützen, für Anweisungen, für die sie geeignet ist. Ihr Verhalten ist nicht konsistent über Backends hinweg.
Die Verwendung dieser Methode ist normalerweise nicht erforderlich, wenn insert()-Ausdruckskonstrukte verwendet werden; das Attribut CursorResult.inserted_primary_key stellt ein Tupel von Primärschlüsselwerten für eine neu eingefügte Zeile bereit, unabhängig vom Datenbank-Backend.
Führt dieses Result mit anderen kompatiblen Ergebnisobjekten zusammen.
Das zurückgegebene Objekt ist eine Instanz von MergedResult, die aus Iteratoren der angegebenen Ergebnisobjekte besteht.
Das neue Ergebnis verwendet die Metadaten dieses Ergebnisobjekts. Die nachfolgenden Ergebnisobjekte müssen mit denselben Metadaten für Ergebnis/Cursor übereinstimmen, andernfalls ist das Verhalten undefiniert.
Genau eine Zeile zurückgeben oder eine Ausnahme auslösen.
Löst NoResultFound aus, wenn das Ergebnis keine Zeilen zurückgibt, oder MultipleResultsFound, wenn mehrere Zeilen zurückgegeben würden.
Hinweis
Diese Methode gibt standardmäßig eine Zeile, z. B. ein Tupel, zurück. Um genau einen einzelnen Skalarwert, d. h. die erste Spalte der ersten Zeile, zurückzugeben, verwenden Sie die Methode Result.scalar_one() oder kombinieren Sie Result.scalars() und Result.one().
Iterieren Sie durch Teillisten von Zeilen der angegebenen Größe.
Jede Liste hat die angegebene Größe, außer der letzten Liste, die ausgegeben wird und möglicherweise nur wenige Zeilen enthält. Es werden keine leeren Listen ausgegeben.
Das Ergebnisobjekt wird automatisch geschlossen, wenn der Iterator vollständig verbraucht ist.
Beachten Sie, dass der Backend-Treiber normalerweise das gesamte Ergebnis im Voraus puffert, es sei denn, die Ausführungsoption Connection.execution_options.stream_results wird verwendet, die angibt, dass der Treiber die Ergebnisse nach Möglichkeit nicht im Voraus puffern soll. Nicht alle Treiber unterstützen diese Option und die Option wird für diejenigen, die dies nicht tun, stillschweigend ignoriert.
Bei Verwendung der ORM ist die Methode Result.partitions() aus Speichersicht typischerweise effektiver, wenn sie mit der Ausführungsoption yield_per kombiniert wird, die sowohl den DBAPI-Treiber zur Verwendung von serverseitigen Cursorn (falls verfügbar) anweist, als auch die ORM-Ladeinternals anweist, nur eine bestimmte Menge von ORM-Objekten aus einem Ergebnis auf einmal zu erstellen, bevor sie ausgegeben werden.
Neu in Version 1.4.
Parameter:
Größe¶ – Gibt die maximale Anzahl der Zeilen an, die in jeder übergebenen Liste enthalten sein sollen. Wenn None, wird der Wert verwendet, der von der Result.yield_per()-Methode, falls sie aufgerufen wurde, oder von der Connection.execution_options.yield_per-Ausführungsoption übernommen wird, was in dieser Hinsicht äquivalent ist. Wenn yield_per nicht gesetzt war, wird der Standardwert von Result.fetchmany() verwendet, der backend-spezifisch und nicht gut definiert sein kann.
Gibt die Werte von Standardspalten zurück, die mit der Funktion ValuesBase.return_defaults() abgerufen wurden.
Der Wert ist eine Instanz von Row oder None, wenn ValuesBase.return_defaults() nicht verwendet wurde oder wenn das Backend RETURNING nicht unterstützt.
Gibt eine Liste von Zeilen zurück, die jeweils die Werte von Standardspalten enthalten, die mit der Funktion ValuesBase.return_defaults() abgerufen wurden.
Insgesamt sollte der Wert von CursorResult.returns_rows immer synonym damit sein, ob der DBAPI-Cursor ein Attribut .description hatte, das das Vorhandensein von Ergebnisspalten anzeigt, wobei zu beachten ist, dass ein Cursor, der null Zeilen zurückgibt, immer noch ein .description hat, wenn eine zeilenrückgebende Anweisung emittiert wurde.
Dieses Attribut sollte für alle Ergebnisse von SELECT-Anweisungen sowie für DML-Anweisungen INSERT/UPDATE/DELETE mit RETURNING True sein. Für INSERT/UPDATE/DELETE-Anweisungen ohne RETURNING ist der Wert normalerweise False, es gibt jedoch einige dialekt-spezifische Ausnahmen, z. B. wenn mit dem MSSQL/pyodbc-Dialekt ein SELECT inline emittiert wird, um einen eingefügten Primärschlüsselwert abzurufen.
Der Hauptzweck von ‚rowcount‘ ist die Meldung der von der WHERE-Klausel einer einmal ausgeführten UPDATE- oder DELETE-Anweisung (d. h. für einen einzelnen Parametersatz) übereinstimmenden Zeilen, die dann mit der Anzahl der zu aktualisierenden oder zu löschenden Zeilen verglichen werden kann, um die Datenintegrität zu gewährleisten.
Dieses Attribut wird vom DBAPI-Attribut cursor.rowcount übernommen, bevor der Cursor geschlossen wird, um DBAPIs zu unterstützen, die diesen Wert nach dem Schließen des Cursors nicht verfügbar machen. Einige DBAPIs bieten möglicherweise aussagekräftige Werte für andere Arten von Anweisungen, wie z. B. INSERT- und SELECT-Anweisungen. Um cursor.rowcount für diese Anweisungen abzurufen, setzen Sie die Ausführungsoption Connection.execution_options.preserve_rowcount auf True, wodurch der Wert cursor.rowcount bedingungslos gememoisiert wird, bevor Ergebnisse zurückgegeben oder der Cursor geschlossen wird, unabhängig vom Anweisungstyp.
In Fällen, in denen die DBAPI rowcount für eine bestimmte Art von Anweisung und/oder Ausführung nicht unterstützt, ist der zurückgegebene Wert -1, der direkt von der DBAPI stammt und Teil von PEP 249 ist. Alle DBAPIs sollten rowcount jedoch für einzelne Parametersatz-UPDATE- und DELETE-Anweisungen unterstützen.
Dieses Attribut gibt die Anzahl der *übereinstimmenden* Zeilen zurück, was nicht unbedingt dasselbe ist wie die Anzahl der tatsächlich *geänderten* Zeilen. Zum Beispiel kann eine UPDATE-Anweisung keine Nettoänderung bei einer gegebenen Zeile bewirken, wenn die angegebenen SET-Werte dieselben sind wie die bereits in der Zeile vorhandenen. Eine solche Zeile würde übereinstimmen, aber nicht geändert werden. Auf Backends, die beide Stile bieten, wie z. B. MySQL, ist rowcount so konfiguriert, dass in allen Fällen die Anzahl der Übereinstimmungen zurückgegeben wird.
CursorResult.rowcount ist im Standardfall *nur* in Verbindung mit einer UPDATE- oder DELETE-Anweisung und nur mit einem einzelnen Satz von Parametern nützlich. Für andere Arten von Anweisungen versucht SQLAlchemy nicht, den Wert vorab zu memoen, es sei denn, die Ausführungsoption Connection.execution_options.preserve_rowcount wird verwendet. Beachten Sie, dass im Gegensatz zu PEP 249 viele DBAPIs keine rowcount-Werte für andere Anweisungen als UPDATE oder DELETE unterstützen, insbesondere wenn Zeilen zurückgegeben werden, die nicht vollständig vorab gepuffert sind. DBAPIs, die rowcount für eine bestimmte Art von Anweisung nicht unterstützen, sollten für solche Anweisungen den Wert -1 zurückgeben.
CursorResult.rowcount ist möglicherweise nicht aussagekräftig, wenn eine einzelne Anweisung mit mehreren Parametersätzen ausgeführt wird (d. h. ein executemany). Die meisten DBAPIs summieren die "rowcount"-Werte nicht über mehrere Parametersätze und geben beim Zugriff -1 zurück.
Gibt ein ScalarResult-Filterobjekt zurück, das einzelne Elemente anstelle von Row-Objekten zurückgibt.
Z. B.
>>> result=conn.execute(text("select int_id from table"))>>> result.scalars().all()[1, 2, 3]
Wenn Ergebnisse aus dem ScalarResult-Filterobjekt abgerufen werden, wird die einzelne Spalte/Zeile, die vom Result zurückgegeben würde, stattdessen als Wert der Spalte zurückgegeben.
Neu in Version 1.4.
Parameter:
index¶ – Ganzzahl oder Zeilenschlüssel, der die abzurufende Spalte aus jeder Zeile angibt, standardmäßig 0, was die erste Spalte angibt.
Gibt zurück:
ein neues ScalarResult-Filterobjekt, das sich auf dieses Result-Objekt bezieht.
Diese Methode ist für die SQLAlchemy ORM und nicht für den allgemeinen Gebrauch bestimmt.
"Horizontal verbinden" bedeutet, dass für jede Zeile im ersten und zweiten Ergebnissatz eine neue Zeile erzeugt wird, die die beiden Zeilen zusammenfügt, die dann die neue Zeile bildet. Der eingehende CursorResult muss die identische Anzahl von Zeilen haben. Es wird typischerweise erwartet, dass die beiden Ergebnissätze auch aus der gleichen Sortierreihenfolge stammen, da die Ergebniszeilen basierend auf ihrer Position im Ergebnis verbunden werden.
Der erwartete Anwendungsfall ist, dass mehrere INSERT..RETURNING-Anweisungen (die definitiv sortiert werden müssen) für verschiedene Tabellen ein einzelnes Ergebnis liefern können, das wie ein JOIN dieser beiden Tabellen aussieht.
Gibt ein neues CursorResult zurück, das die Zeilen dieses CursorResult mit denen eines anderen CursorResult "vertikal verbindet", d. h. "erweitert".
Tipp
Diese Methode ist für die SQLAlchemy ORM und nicht für den allgemeinen Gebrauch bestimmt.
"Vertikal verbinden" bedeutet, dass die Zeilen des gegebenen Ergebnisses an die Zeilen dieses Cursor-Ergebnisses angehängt werden. Der eingehende CursorResult muss Zeilen haben, die die identische Liste von Spalten in der identischen Reihenfolge darstellen, wie sie in diesem CursorResult vorhanden sind.
Wendet einen "typisierten Tupel"-Typisierungsfilter auf zurückgegebene Zeilen an.
Diese Methode gibt zur Laufzeit dasselbe Result-Objekt zurück, annotiert aber als Rückgabe eines TupleResult-Objekts, das Typisierungswerkzeugen nach PEP 484 anzeigt, dass einfache typisierte Tuple-Instanzen anstelle von Zeilen zurückgegeben werden. Dies ermöglicht Tupel-Entpackung und __getitem__-Zugriff auf Row-Objekte, für Fälle, in denen die aufgerufene Anweisung selbst Typisierungsinformationen enthielt.
Anwenden eines eindeutigen Filters auf die von diesem Result zurückgegebenen Objekte.
Wenn dieser Filter ohne Argumente angewendet wird, werden die zurückgegebenen Zeilen oder Objekte so gefiltert, dass jede Zeile eindeutig zurückgegeben wird. Der Algorithmus zur Bestimmung dieser Einzigartigkeit ist standardmäßig die Python-Hashing-Identität des gesamten Tupels. In einigen Fällen kann ein spezialisiertes pro-Entitäts-Hashing-Schema verwendet werden, z. B. bei der Verwendung der ORM, wobei ein Schema angewendet wird, das gegen die Primärschlüssel-Identität der zurückgegebenen Objekte arbeitet.
Der eindeutige Filter wird **nach allen anderen Filtern** angewendet. Das bedeutet, wenn die zurückgegebenen Spalten mit einer Methode wie Result.columns() oder Result.scalars() verfeinert wurden, wird die Einzigartigkeit **nur auf die zurückgegebene Spalte oder Spalten** angewendet. Dies geschieht unabhängig von der Reihenfolge, in der diese Methoden auf das Result-Objekt angewendet wurden.
Der eindeutige Filter ändert auch die Berechnungslogik, die für Methoden wie Result.fetchmany() und Result.partitions() verwendet wird. Bei Verwendung von Result.unique() werden diese Methoden weiterhin die angeforderte Anzahl von Zeilen oder Objekten liefern, nachdem die Eindeutigkeit angewendet wurde. Dies beeinträchtigt jedoch zwangsläufig das Pufferverhalten des zugrunde liegenden Cursors oder der Datenquelle, sodass mehrere zugrunde liegende Aufrufe von cursor.fetchmany() erforderlich sein können, um genügend Objekte zu sammeln, um eine eindeutige Sammlung der angeforderten Größe bereitzustellen.
Parameter:
strategy¶ – Ein aufrufbares Objekt, das auf die iterierten Zeilen oder Objekte angewendet wird und ein Objekt zurückgeben sollte, das den eindeutigen Wert der Zeile repräsentiert. Ein Python set() wird verwendet, um diese Identitäten zu speichern. Wenn nichts übergeben wird, wird eine Standard-Eindeutigkeitsstrategie verwendet, die möglicherweise von der Quelle dieses Result-Objekts zusammengestellt wurde.
Konfiguriert die Strategie zum Abrufen von Zeilen, um num Zeilen auf einmal abzurufen.
Dies beeinflusst das zugrunde liegende Verhalten des Ergebnisses, wenn über das Ergebnisobjekt iteriert wird oder Methoden wie Result.fetchone() verwendet werden, die jeweils eine Zeile zurückgeben. Daten aus dem zugrunde liegenden Cursor oder einer anderen Datenquelle werden bis zu dieser Anzahl von Zeilen im Speicher gepuffert, und die gepufferte Sammlung wird dann zeilenweise oder in der angeforderten Anzahl von Zeilen ausgegeben. Jedes Mal, wenn der Puffer geleert wird, wird er mit dieser Anzahl von Zeilen aufgefüllt oder mit weniger Zeilen, wenn weniger vorhanden sind.
Die Methode Result.yield_per() wird im Allgemeinen in Verbindung mit der Ausführungsoption Connection.execution_options.stream_results verwendet, die es dem verwendeten Datenbankdialekt ermöglicht, einen serverseitigen Cursor zu nutzen, falls die DBAPI einen spezifischen „serverseitigen Cursor“-Modus unterstützt, der sich von seinem Standardbetrieb unterscheidet.
num¶ – Anzahl der Zeilen, die jedes Mal abgerufen werden sollen, wenn der Puffer neu gefüllt wird. Wenn auf einen Wert unter 1 gesetzt, werden alle Zeilen für den nächsten Puffer abgerufen.
New in version 1.4: Das Objekt Result bietet ein vollständig aktualisiertes Nutzungsmodell und eine aufrufende Schnittstelle für SQLAlchemy Core und SQLAlchemy ORM. In Core bildet es die Basis des Objekts CursorResult, das die vorherige Schnittstelle ResultProxy ersetzt. Bei Verwendung des ORM wird normalerweise ein höheres Objekt namens ChunkedIteratorResult verwendet.
Hinweis
In SQLAlchemy 1.4 und höher wird dieses Objekt für ORM-Ergebnisse verwendet, die von Session.execute() zurückgegeben werden, welche ORM-Objekte entweder einzeln oder in Tupel-ähnlichen Zeilen liefern können. Beachten Sie, dass das Objekt Result Instanzen oder Zeilen nicht automatisch dedupliziert, wie es bei der Legacy Query der Fall war. Für die Deduplizierung von Instanzen oder Zeilen in Python verwenden Sie die Modifier-Methode Result.unique().
Das Verhalten dieser Methode ist implementierungsspezifisch und wird standardmäßig nicht implementiert. Die Methode sollte im Allgemeinen die vom Ergebnisobjekt verwendeten Ressourcen beenden und bewirken, dass jede nachfolgende Iteration oder Zeilenabfrage eine ResourceClosedError auslöst.
New in version 1.4.27: - .close() war zuvor nicht allgemein für alle Result-Klassen verfügbar, sondern nur für das CursorResult, das für Core-Statement-Ausführungen zurückgegeben wurde. Da die meisten anderen Ergebnisobjekte, insbesondere die vom ORM verwendeten, ohnehin ein CursorResult proxyen, ermöglicht dies das Schließen des zugrunde liegenden Cursorergebnisses von der externen Fassade für den Fall, dass die ORM-Abfrage die Ausführungsoption yield_per verwendet, bei der der Datenbankcursor nicht sofort erschöpft und automatisch geschlossen wird.
Definieren Sie die Spalten, die in jeder Zeile zurückgegeben werden sollen.
Diese Methode kann verwendet werden, um die zurückgegebenen Spalten zu begrenzen und sie neu zu ordnen. Die angegebenen Ausdrücke sind normalerweise eine Reihe von Ganzzahlen oder Zeichenketten-Schlüsselnamen. Sie können auch geeignete ColumnElement-Objekte sein, die einem gegebenen Anweisungskonstrukt entsprechen.
Geändert in Version 2.0: Aufgrund eines Fehlers in 1.4 verhielt sich die Methode Result.columns() fehlerhaft, da der Aufruf der Methode mit nur einem Index dazu führte, dass das Result-Objekt Skalarwerte anstelle von Row-Objekten lieferte. In Version 2.0 wurde dieses Verhalten korrigiert, sodass der Aufruf von Result.columns() mit einem einzelnen Index ein Result-Objekt erzeugt, das weiterhin Row-Objekte liefert, die nur eine einzelne Spalte enthalten.
*col_expressions¶ – gibt die zurückzugebenden Spalten an. Elemente können ganzzahlige Zeilenindizes, Spaltennamen oder entsprechende ColumnElement-Objekte sein, die einem SELECT-Konstrukt entsprechen.
Gibt zurück:
dieses Result-Objekt mit den gegebenen Modifikationen.
Wenn alle Zeilen erschöpft sind, wird None zurückgegeben.
Diese Methode wird zur Abwärtskompatibilität mit SQLAlchemy 1.x.x bereitgestellt.
Um nur die erste Zeile eines Ergebnisses abzurufen, verwenden Sie die Methode Result.first(). Um durch alle Zeilen zu iterieren, iterieren Sie direkt über das Result-Objekt.
Gibt zurück:
ein Row-Objekt, wenn keine Filter angewendet werden, oder None, wenn keine Zeilen mehr vorhanden sind.
Die erste Zeile abrufen oder None, wenn keine Zeile vorhanden ist.
Schließt den Ergebnissatz und verwirft die restlichen Zeilen.
Hinweis
Diese Methode gibt standardmäßig eine Zeile, z. B. ein Tupel, zurück. Um genau einen einzelnen Skalarwert, d. h. die erste Spalte der ersten Zeile, zurückzugeben, verwenden Sie die Methode Result.scalar() oder kombinieren Sie Result.scalars() und Result.first().
Zusätzlich, im Gegensatz zum Verhalten der Legacy-ORM-Methode Query.first(), wird **keine Begrenzung** auf die SQL-Abfrage angewendet, die zur Erzeugung dieses Result aufgerufen wurde; für einen DBAPI-Treiber, der Ergebnisse im Speicher puffert, bevor Zeilen ausgegeben werden, werden alle Zeilen an den Python-Prozess gesendet und alle außer der ersten Zeile werden verworfen.
Gibt ein aufrufbares Objekt zurück, das Kopien dieses Result erzeugt, wenn es aufgerufen wird.
Das zurückgegebene aufrufbare Objekt ist eine Instanz von FrozenResult.
Dies wird für das Caching von Ergebnismengen verwendet. Die Methode muss aufgerufen werden, wenn das Ergebnis noch nicht konsumiert wurde, und der Aufruf der Methode verbraucht das Ergebnis vollständig. Wenn die FrozenResult aus einem Cache abgerufen wird, kann sie beliebig oft aufgerufen werden, wobei jedes Mal ein neues Result-Objekt gegen den gespeicherten Zeilensatz erzeugt wird.
Siehe auch
Anweisungen neu ausführen - Beispiel für die Verwendung in der ORM zur Implementierung eines Ergebnis-Caching.
geerbt von dersqlalchemy.engine._WithKeys.keysMethode vonsqlalchemy.engine._WithKeys
Gibt eine iterierbare Ansicht zurück, die die Zeichenketten-Schlüssel liefert, die von jedem Row dargestellt würden.
Die Schlüssel können die Bezeichnungen der von einer Core-Anweisung zurückgegebenen Spalten oder die Namen der von einer ORM-Ausführung zurückgegebenen ORM-Klassen darstellen.
Die Ansicht kann auch mithilfe des Python-Operators in auf Schlüsselcontainment getestet werden, wobei sowohl die Zeichenketten-Schlüssel, die in der Ansicht dargestellt sind, als auch alternative Schlüssel wie Spaltenobjekte getestet werden.
Geändert in Version 1.4: Es wird ein Key-View-Objekt zurückgegeben, anstatt einer einfachen Liste.
Führt dieses Result mit anderen kompatiblen Ergebnisobjekten zusammen.
Das zurückgegebene Objekt ist eine Instanz von MergedResult, die aus Iteratoren der angegebenen Ergebnisobjekte besteht.
Das neue Ergebnis verwendet die Metadaten dieses Ergebnisobjekts. Die nachfolgenden Ergebnisobjekte müssen mit denselben Metadaten für Ergebnis/Cursor übereinstimmen, andernfalls ist das Verhalten undefiniert.
Genau eine Zeile zurückgeben oder eine Ausnahme auslösen.
Löst NoResultFound aus, wenn das Ergebnis keine Zeilen zurückgibt, oder MultipleResultsFound, wenn mehrere Zeilen zurückgegeben würden.
Hinweis
Diese Methode gibt standardmäßig eine Zeile, z. B. ein Tupel, zurück. Um genau einen einzelnen Skalarwert, d. h. die erste Spalte der ersten Zeile, zurückzugeben, verwenden Sie die Methode Result.scalar_one() oder kombinieren Sie Result.scalars() und Result.one().
Iterieren Sie durch Teillisten von Zeilen der angegebenen Größe.
Jede Liste hat die angegebene Größe, außer der letzten Liste, die ausgegeben wird und möglicherweise nur wenige Zeilen enthält. Es werden keine leeren Listen ausgegeben.
Das Ergebnisobjekt wird automatisch geschlossen, wenn der Iterator vollständig verbraucht ist.
Beachten Sie, dass der Backend-Treiber normalerweise das gesamte Ergebnis im Voraus puffert, es sei denn, die Ausführungsoption Connection.execution_options.stream_results wird verwendet, die angibt, dass der Treiber die Ergebnisse nach Möglichkeit nicht im Voraus puffern soll. Nicht alle Treiber unterstützen diese Option und die Option wird für diejenigen, die dies nicht tun, stillschweigend ignoriert.
Bei Verwendung der ORM ist die Methode Result.partitions() aus Speichersicht typischerweise effektiver, wenn sie mit der Ausführungsoption yield_per kombiniert wird, die sowohl den DBAPI-Treiber zur Verwendung von serverseitigen Cursorn (falls verfügbar) anweist, als auch die ORM-Ladeinternals anweist, nur eine bestimmte Menge von ORM-Objekten aus einem Ergebnis auf einmal zu erstellen, bevor sie ausgegeben werden.
Neu in Version 1.4.
Parameter:
size¶ – gibt die maximale Anzahl von Zeilen an, die in jeder gelieferten Liste enthalten sein sollen. Wenn None, wird der von der Methode Result.yield_per() gesetzte Wert verwendet, falls diese aufgerufen wurde, oder die Ausführungsoption Connection.execution_options.yield_per, welche in diesem Zusammenhang äquivalent ist. Wenn yield_per nicht gesetzt wurde, wird der Standardwert von Result.fetchmany() verwendet, welcher backend-spezifisch und nicht gut definiert sein kann.
Gibt ein ScalarResult-Filterobjekt zurück, das einzelne Elemente anstelle von Row-Objekten zurückgibt.
Z. B.
>>> result=conn.execute(text("select int_id from table"))>>> result.scalars().all()[1, 2, 3]
Wenn Ergebnisse aus dem ScalarResult-Filterobjekt abgerufen werden, wird die einzelne Spalte/Zeile, die vom Result zurückgegeben würde, stattdessen als Wert der Spalte zurückgegeben.
Neu in Version 1.4.
Parameter:
index¶ – Ganzzahl oder Zeilenschlüssel, der die zu holende Spalte aus jeder Zeile angibt, standardmäßig 0, was die erste Spalte angibt.
Gibt zurück:
ein neues ScalarResult-Filterobjekt, das sich auf dieses Result-Objekt bezieht.
Wendet einen "typisierten Tupel"-Typisierungsfilter auf zurückgegebene Zeilen an.
Diese Methode gibt dasselbe Result Objekt zur Laufzeit zurück, annotiert aber als Rückgabe eines TupleResult Objekts, das Typwerkzeugen gemäß PEP 484 mitteilt, dass einfache typisierte Tuple Instanzen anstelle von Zeilen zurückgegeben werden. Dies ermöglicht Tupel-Entpackung und __getitem__ Zugriff auf Row Objekte, die typisiert werden können, für Fälle, in denen die aufgerufene Anweisung selbst Typinformationen enthielt.
Anwenden eines eindeutigen Filters auf die von diesem Result zurückgegebenen Objekte.
Wenn dieser Filter ohne Argumente angewendet wird, werden die zurückgegebenen Zeilen oder Objekte so gefiltert, dass jede Zeile eindeutig zurückgegeben wird. Der Algorithmus zur Bestimmung dieser Einzigartigkeit ist standardmäßig die Python-Hashing-Identität des gesamten Tupels. In einigen Fällen kann ein spezialisiertes pro-Entitäts-Hashing-Schema verwendet werden, z. B. bei der Verwendung der ORM, wobei ein Schema angewendet wird, das gegen die Primärschlüssel-Identität der zurückgegebenen Objekte arbeitet.
Der eindeutige Filter wird **nach allen anderen Filtern** angewendet. Das bedeutet, wenn die zurückgegebenen Spalten mit einer Methode wie Result.columns() oder Result.scalars() verfeinert wurden, wird die Einzigartigkeit **nur auf die zurückgegebene Spalte oder Spalten** angewendet. Dies geschieht unabhängig von der Reihenfolge, in der diese Methoden auf das Result-Objekt angewendet wurden.
Der eindeutige Filter ändert auch die Berechnungslogik, die für Methoden wie Result.fetchmany() und Result.partitions() verwendet wird. Bei Verwendung von Result.unique() werden diese Methoden weiterhin die angeforderte Anzahl von Zeilen oder Objekten liefern, nachdem die Eindeutigkeit angewendet wurde. Dies beeinträchtigt jedoch zwangsläufig das Pufferverhalten des zugrunde liegenden Cursors oder der Datenquelle, sodass mehrere zugrunde liegende Aufrufe von cursor.fetchmany() erforderlich sein können, um genügend Objekte zu sammeln, um eine eindeutige Sammlung der angeforderten Größe bereitzustellen.
Parameter:
strategy¶ – ein aufrufbares Objekt, das auf die zu iterierenden Zeilen oder Objekte angewendet wird und ein Objekt zurückgeben soll, das den eindeutigen Wert der Zeile repräsentiert. Ein Python set() wird verwendet, um diese Identitäten zu speichern. Wenn kein Wert übergeben wird, wird eine Standard-Eindeutigkeitsstrategie verwendet, die möglicherweise von der Quelle dieses Result Objekts zusammengestellt wurde.
Konfiguriert die Strategie zum Abrufen von Zeilen, um num Zeilen auf einmal abzurufen.
Dies beeinflusst das zugrunde liegende Verhalten des Ergebnisses, wenn über das Ergebnisobjekt iteriert wird oder Methoden wie Result.fetchone() verwendet werden, die jeweils eine Zeile zurückgeben. Daten aus dem zugrunde liegenden Cursor oder einer anderen Datenquelle werden bis zu dieser Anzahl von Zeilen im Speicher gepuffert, und die gepufferte Sammlung wird dann zeilenweise oder in der angeforderten Anzahl von Zeilen ausgegeben. Jedes Mal, wenn der Puffer geleert wird, wird er mit dieser Anzahl von Zeilen aufgefüllt oder mit weniger Zeilen, wenn weniger vorhanden sind.
Die Methode Result.yield_per() wird im Allgemeinen in Verbindung mit der Ausführungsoption Connection.execution_options.stream_results verwendet, die es dem verwendeten Datenbankdialekt ermöglicht, einen serverseitigen Cursor zu nutzen, falls die DBAPI einen spezifischen „serverseitigen Cursor“-Modus unterstützt, der sich von seinem Standardbetrieb unterscheidet.
num¶ – Anzahl der Zeilen, die jedes Mal geholt werden sollen, wenn der Puffer neu gefüllt wird. Wenn dieser Wert kleiner als 1 gesetzt wird, werden alle Zeilen für den nächsten Puffer geholt.
Eine besondere Einschränkung von ScalarResult ist, dass es keine fetchone() Methode hat; da die Semantik von fetchone() darin besteht, dass der Wert None keine weiteren Ergebnisse anzeigt, ist dies nicht mit ScalarResult kompatibel, da nicht zwischen None als Zeilenwert und None als Indikator unterschieden werden kann. Verwenden Sie next(result), um Werte einzeln zu empfangen.
geerbt von dersqlalchemy.engine._WithKeys.keysMethode vonsqlalchemy.engine._WithKeys
Gibt eine iterierbare Ansicht zurück, die die Zeichenketten-Schlüssel liefert, die von jedem Row dargestellt würden.
Die Schlüssel können die Bezeichnungen der von einer Core-Anweisung zurückgegebenen Spalten oder die Namen der von einer ORM-Ausführung zurückgegebenen ORM-Klassen darstellen.
Die Ansicht kann auch mithilfe des Python-Operators in auf Schlüsselcontainment getestet werden, wobei sowohl die Zeichenketten-Schlüssel, die in der Ansicht dargestellt sind, als auch alternative Schlüssel wie Spaltenobjekte getestet werden.
Geändert in Version 1.4: Es wird ein Key-View-Objekt zurückgegeben, anstatt einer einfachen Liste.
Das Row-Objekt repräsentiert eine Zeile eines Datenbankergebnisses. Es ist typischerweise in der 1.x-Serie von SQLAlchemy mit dem CursorResult-Objekt verbunden, wird aber ab SQLAlchemy 1.4 auch von der ORM für Tupel-ähnliche Ergebnisse verwendet.
Das Row-Objekt versucht, sich so gut wie möglich wie ein Python-benanntes Tupel zu verhalten. Für Mapping- (d.h. Wörterbuch-) Verhalten bei einer Zeile, wie z.B. das Testen auf Enthaltung von Schlüsseln, siehe das Attribut Row._mapping.
Gibt ein neues Wörterbuch zurück, das Feldnamen ihren entsprechenden Werten zuordnet.
Diese Methode ist analog zur Methode ._asdict() des Python-benannten Tupels und funktioniert, indem der Konstruktor dict() auf das Attribut Row._mapping angewendet wird.
Gibt ein Tupel von Zeichenketten-Schlüsseln zurück, wie sie von diesem Row dargestellt werden.
Die Schlüssel können die Bezeichnungen der von einer Core-Anweisung zurückgegebenen Spalten oder die Namen der von einer ORM-Ausführung zurückgegebenen ORM-Klassen darstellen.
Dieses Attribut ist analog zum Attribut ._fields des Python-benannten Tupels.
Dieses Objekt bietet eine konsistente Python-Mapping- (d.h. Wörterbuch-) Schnittstelle für die in der Zeile enthaltenen Daten. Die Row selbst verhält sich wie ein benanntes Tupel.
Neu in Version 2.0.19: - Das Attribut Row._t ersetzt das vorherige Attribut Row.t, das nun unterstrichen ist, um Namenskonflikte mit Spaltennamen zu vermeiden, genauso wie andere benannte Tupel-Methoden von Row.
Zur Laufzeit gibt diese Methode "self" zurück; das Row-Objekt ist bereits ein benanntes Tupel. Auf der Typing-Ebene ist jedoch, wenn diese Row typisiert ist, der "Tupel"-Rückgabetyp ein PEP 484Tuple-Datentyp, der Typinformationen über einzelne Elemente enthält und typisiertes Entpacken und Attributzugriff unterstützt.
Neu in Version 2.0.19: - Die Methode Row._tuple() ersetzt die vorherige Methode Row.tuple(), die nun unterstrichen ist, um Namenskonflikte mit Spaltennamen zu vermeiden, genauso wie andere benannte Tupel-Methoden von Row.
Veraltet seit Version 2.0.19: Das Attribut Row.t ist zugunsten von Row._t veraltet; alle Methoden und bibliotheksweiten Attribute von Row sind zur Vermeidung von Namenskonflikten unterstrichen. Bitte verwenden Sie Row._t.
Veraltet seit Version 2.0.19: Die Methode Row.tuple() ist zugunsten von Row._tuple() veraltet; alle Methoden und bibliotheksweiten Attribute von Row sind zur Vermeidung von Namenskonflikten unterstrichen. Bitte verwenden Sie Row._tuple().
RowMapping stellt Python-Mapping- (d.h. Wörterbuch-) Zugriff auf die Inhalte der Zeile bereit. Dies beinhaltet die Unterstützung für die Prüfung der Enthaltung bestimmter Schlüssel (Zeichenketten-Spaltennamen oder -Objekte) sowie die Iteration von Schlüsseln, Werten und Elementen.
Neu in Version 1.4: Das Objekt RowMapping ersetzt den Mapping-ähnlichen Zugriff, der zuvor von einer Datenbankergebniszeile bereitgestellt wurde, die sich nun größtenteils wie ein benanntes Tupel verhält.
Ein Result, der als Rückgabe von einfachen Python-Tupeln statt Zeilen typisiert ist.
Da Row sich bereits wie ein Tupel verhält, ist diese Klasse eine reine Typisierungsklasse; zur Laufzeit wird immer noch ein reguläres Result verwendet.