SQLAlchemy: Engine-, Verbindungs- und Sitzungsunterschied

134

Ich verwende SQLAlchemy und es gibt mindestens drei Einheiten: engine, sessionund connection, die haben executeMethode, also wenn ich zum Beispiel möchte alle Datensätze auswählen aus tableich das tun kann

engine.execute(select([table])).fetchall()

und das

connection.execute(select([table])).fetchall()

und sogar das

session.execute(select([table])).fetchall()

- Die Ergebnisse sind gleich.

Soweit ich weiß, wird engine.executees erstellt , wenn jemand es verwendet connection, öffnet session(Alchemy kümmert sich für Sie darum) und führt die Abfrage aus. Aber gibt es einen globalen Unterschied zwischen diesen drei Arten, eine solche Aufgabe auszuführen?

Ololobus
quelle
Ich denke, Ihre Antwort ist hier richtig: hackersandslackers.com/…
SeF

Antworten:

123

Eine einzeilige Übersicht:

Das Verhalten execute()ist das gleiche in allen Fällen, aber sie sind 3 verschiedene Methoden, in Engine, Connectionund SessionKlassen.

Was genau ist execute():

Um das Verhalten von zu verstehen execute(), müssen wir in die ExecutableKlasse schauen . Executableist eine Oberklasse für alle "Anweisung" -Objekttypen, einschließlich select (), delete (), update (), insert (), text () - in einfachsten Worten Executableist es ein SQL-Ausdruckskonstrukt, das in SQLAlchemy unterstützt wird.

In allen Fällen verwendet die execute()Methode den SQL-Text oder den konstruierten SQL-Ausdruck, dh eines der verschiedenen in SQLAlchemy unterstützten SQL-Ausdruckskonstrukte, und gibt Abfrageergebnisse zurück (a ResultProxy- Umschließt ein DB-APICursorobjekt, um den Zugriff auf Zeilenspalten zu erleichtern.)


Zur weiteren Klärung (nur zur konzeptionellen Klärung, kein empfohlener Ansatz) :

Zusätzlich zu Engine.execute()(verbindungslose Ausführung), Connection.execute()und Session.execute(), ist es auch möglich, das execute()direkt auf jedem ExecutableKonstrukt zu verwenden. Die ExecutableKlasse hat eine eigene Implementierung von execute()- Gemäß der offiziellen Dokumentation execute()lautet eine einzeilige Beschreibung " Kompilieren und AusführenExecutable ". In diesem Fall müssen wir das Executable(SQL-Ausdruckskonstrukt) explizit an ein ConnectionObjekt oder ein EngineObjekt (das implizit ein ConnectionObjekt erhält ) binden , damit der execute()weiß, wo das ausgeführt werden soll SQL.

Das folgende Beispiel zeigt es gut - Gegeben eine Tabelle wie folgt:

from sqlalchemy import MetaData, Table, Column, Integer

meta = MetaData()
users_table = Table('users', meta,
    Column('id', Integer, primary_key=True),
    Column('name', String(50)))

Explizite Ausführung, dh Connection.execute()Übergabe des SQL-Textes oder des erstellten SQL-Ausdrucks an die execute()Methode Connection:

engine = create_engine('sqlite:///file.db')
connection = engine.connect()
result = connection.execute(users_table.select())
for row in result:
    # ....
connection.close()

Explizite verbindungslose Ausführung, dh Engine.execute()Übergabe des SQL-Textes oder des erstellten SQL-Ausdrucks direkt an die execute()Engine-Methode:

engine = create_engine('sqlite:///file.db')
result = engine.execute(users_table.select())
for row in result:
    # ....
result.close()

Die implizite Ausführung, dh Executable.execute()- ist ebenfalls verbindungslos und ruft die execute()Methode von auf Executable, dh sie ruft die execute()Methode direkt für das SQLAusdruckskonstrukt (eine Instanz von Executable) selbst auf.

engine = create_engine('sqlite:///file.db')
meta.bind = engine
result = users_table.select().execute()
for row in result:
    # ....
result.close()

Hinweis: Das implizite Ausführungsbeispiel wurde zur Verdeutlichung angegeben - diese Art der Ausführung wird dringend empfohlen - gemäß den folgenden Dokumenten :

"Implizite Ausführung" ist ein sehr altes Verwendungsmuster, das in den meisten Fällen eher verwirrend als hilfreich ist und von dessen Verwendung abgeraten wird. Beide Muster scheinen die Überbeanspruchung zweckmäßiger „Abkürzungen“ im Anwendungsdesign zu fördern, die später zu Problemen führen.


Deine Fragen:

Soweit ich weiß, stellt jemand, der engine.execute verwendet, eine Verbindung her, öffnet eine Sitzung (Alchemy kümmert sich um Sie) und führt eine Abfrage aus.

Sie haben Recht für den Teil "Wenn jemand ihn verwendet engine.execute, erstellt connection", aber nicht für "Öffnen session(Alchemy kümmert sich um Sie) und Ausführen der Abfrage" - Verwenden Engine.execute()und Connection.execute()ist (fast) dasselbe, formal wird das ConnectionObjekt implizit erstellt und im späteren Fall instanziieren wir es explizit. Was in diesem Fall wirklich passiert, ist:

`Engine` object (instantiated via `create_engine()`) -> `Connection` object (instantiated via `engine_instance.connect()`) -> `connection.execute({*SQL expression*})`

Aber gibt es einen globalen Unterschied zwischen diesen drei Arten, eine solche Aufgabe auszuführen?

Auf der DB-Ebene ist es genau dasselbe, alle führen SQL aus (Textausdruck oder verschiedene SQL-Ausdruckskonstrukte). Aus Sicht der Anwendung gibt es zwei Möglichkeiten:

  • Direkte Ausführung - Mit Engine.execute()oderConnection.execute()
  • Mit sessions- effizient behandelt Transaktion als einzelne Einheit-of-Arbeit, mit Leichtigkeit über session.add(), session.rollback(), session.commit(), session.close(). Dies ist die Möglichkeit, bei ORM, dh zugeordneten Tabellen, mit der Datenbank zu interagieren. Bietet identity_map für den sofortigen Zugriff auf bereits aufgerufene oder neu erstellte / hinzugefügte Objekte während einer einzelnen Anforderung.

Session.execute()Verwendet letztendlich die Connection.execute()Anweisungsausführungsmethode, um die SQL-Anweisung auszuführen. Die Verwendung von SessionObjekten ist die von SQLAlchemy ORM empfohlene Methode für die Interaktion einer Anwendung mit der Datenbank.

Ein Auszug aus den Dokumenten :

Es ist wichtig zu beachten, dass bei Verwendung des SQLAlchemy ORM im Allgemeinen nicht auf diese Objekte zugegriffen wird. Stattdessen wird das Sitzungsobjekt als Schnittstelle zur Datenbank verwendet. Für Anwendungen, die auf der direkten Verwendung von textuellen SQL-Anweisungen und / oder SQL-Ausdruckskonstrukten ohne Beteiligung der übergeordneten Verwaltungsdienste des ORM basieren, sind Engine und Verbindung König (und Königin?) - lesen Sie weiter.

Nabeel Ahmed
quelle
"verbindungsloses" Wort impliziert, dass keine Verbindung hergestellt wird, was laut Neals Antwort nicht der Fall ist.
Atom
110

Nabeels Antwort umfasst viele Details und ist hilfreich, aber ich fand es verwirrend, zu folgen. Da dies derzeit das erste Google-Ergebnis für dieses Problem ist, füge ich mein Verständnis für zukünftige Personen hinzu, die diese Frage finden:

Ausführen von .execute ()

Wie OP und Nabell Ahmed beide bemerken, gibt es bei der Ausführung einer Ebene SELECT * FROM tablenamekeinen Unterschied im bereitgestellten Ergebnis.

Die Unterschiede zwischen diesen drei Objekten wichtig würden je nach Kontext , dass die SELECTAussage in oder, allgemein mehr verwendet wird, wenn Sie andere Dinge tun wollen INSERT, DELETEusw.

Wann wird Engine, Connection, Session allgemein verwendet?

  • Engine ist das von SQLAlchemy verwendete Objekt der untersten Ebene. Es verwaltet einen Pool von Verbindungen, die zur Verwendung verfügbar sind, wenn die Anwendung mit der Datenbank kommunizieren muss. .execute()ist eine bequeme Methode, die zuerst conn = engine.connect(close_with_result=True)und dann aufruft conn.execute(). Der Parameter close_with_result bedeutet, dass die Verbindung automatisch geschlossen wird. (Ich paraphrasiere den Quellcode leicht, aber im Wesentlichen wahr). edit: Hier ist der Quellcode für engine.execute

    Sie können die Engine verwenden, um Raw-SQL auszuführen.

    result = engine.execute('SELECT * FROM tablename;')
    #what engine.execute() is doing under the hood
    conn = engine.connect(close_with_result=True)
    result = conn.execute('SELECT * FROM tablename;')
    
    #after you iterate over the results, the result and connection get closed
    for row in result:
        print(result['columnname']
    
    #or you can explicitly close the result, which also closes the connection
    result.close()

    Dies wird in den Dokumenten unter Grundlegende Verwendung behandelt .

  • Die Verbindung ist (wie wir oben gesehen haben) die Sache, die tatsächlich die Arbeit der Ausführung einer SQL-Abfrage erledigt. Sie sollten dies tun, wenn Sie eine bessere Kontrolle über die Attribute der Verbindung wünschen, wenn diese geschlossen wird usw. Ein sehr wichtiges Beispiel hierfür ist beispielsweise eine Transaktion , mit der Sie entscheiden können, wann Ihre Änderungen in die Datenbank übernommen werden sollen. Bei normaler Verwendung werden Änderungen automatisch festgeschrieben. Mit der Verwendung von Transaktionen können Sie (zum Beispiel) mehrere verschiedene SQL-Anweisungen ausführen. Wenn bei einer dieser Anweisungen ein Fehler auftritt, können Sie alle Änderungen gleichzeitig rückgängig machen.

    connection = engine.connect()
    trans = connection.begin()
    try:
        connection.execute("INSERT INTO films VALUES ('Comedy', '82 minutes');")
        connection.execute("INSERT INTO datalog VALUES ('added a comedy');")
        trans.commit()
    except:
        trans.rollback()
        raise

    Auf diese Weise können Sie beide Änderungen rückgängig machen, wenn eine fehlschlägt, z. B. wenn Sie vergessen haben, die Datenprotokolltabelle zu erstellen.

    Wenn Sie also SQL-Rohcode ausführen und Kontrolle benötigen, verwenden Sie Verbindungen

  • Sitzungen werden für den ORM-Aspekt (Object Relationship Management) von SQLAlchemy verwendet (tatsächlich können Sie dies daran erkennen, wie sie importiert werden :) from sqlalchemy.orm import sessionmaker. Sie verwenden Verbindungen und Transaktionen unter der Haube, um ihre automatisch generierten SQL-Anweisungen auszuführen. .execute()ist eine Komfortfunktion, die an alles weitergegeben wird, an das die Sitzung gebunden ist (normalerweise eine Engine, kann aber eine Verbindung sein).

    Wenn Sie die ORM-Funktionalität verwenden, verwenden Sie session. Wenn Sie nur reine SQL-Abfragen ausführen, die nicht an Objekte gebunden sind, ist es wahrscheinlich besser, Verbindungen direkt zu verwenden.

Neal
quelle
1
Sollten Einfügeanweisungen nicht in doppelte Anführungszeichen gesetzt werden ""?
Mingchau
2
@mingchau Ja, Sie haben Recht, meine einfachen Anführungszeichen hätten sich gegenseitig gestört. Doppelte Anführungszeichen sind viel einfacher, um dieses Problem zu vermeiden. Aktualisiert.
Neal
Wie ist meine Sitzung bei einer erstellten Sitzung mit meiner PostgreSQL-Verbindung verknüpft?
Raju yourPepe
@ RajuyourPepe my_session.connection(). Docs: docs.sqlalchemy.org/en/13/orm/… .
Neal
Ernsthaft ? 'Session'-Objekt hat kein Attribut' connect '", habe ich gefunden
Raju yourPepe
0

Hier ist ein Beispiel für die Ausführung von DCL (Data Control Language) wie GRANT

def grantAccess(db, tb, user):
  import sqlalchemy as SA
  import psycopg2

  url = "{d}+{driver}://{u}:{p}@{h}:{port}/{db}".\
            format(d="redshift",
            driver='psycopg2',
            u=username,
            p=password,
            h=host,
            port=port,
            db=db)
  engine = SA.create_engine(url)
  cnn = engine.connect()
  trans = cnn.begin()
  strSQL = "GRANT SELECT on table " + tb + " to " + user + " ;"
  try:
      cnn.execute(strSQL)
      trans.commit()
  except:
      trans.rollback()
      raise
Jie
quelle