web-dev-qa-db-ja.com

SQLAlchemy:エンジン、接続、およびセッションの違い

私はSQLAlchemyを使用し、少なくとも3つのエンティティがあります:enginesession、およびconnectionです。これらにはexecuteメソッドがあります。 tableからすべてのレコードを選択したい

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

この

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

そしてこれでさえ

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

-結果は同じになります。

私が理解しているように、誰かがengine.executeを使用すると、connectionを作成し、session(Alchemyが自動的に処理します)を開き、クエリを実行します。しかし、このようなタスクを実行するこれら3つの方法には、グローバルな違いがありますか?

88
ololobus

一行の概要:

execute()の動作はすべてのケースで同じですが、EngineConnection、およびSessionクラスの3つの異なるメソッドです。

execute()とは正確に:

execute()の動作を理解するには、Executableクラスを調べる必要があります。 Executableは、select()、delete()、update()、insert()、text()など、すべての「ステートメント」タイプのオブジェクトのスーパークラスです。可能な限り単純な言葉で、ExecutableはSQLAlchemyでサポートされるSQL式の構成体です。

すべての場合において、execute()メソッドは、SQLテキストまたは構築されたSQL式、つまりSQLAlchemyでサポートされているさまざまなSQL式の構成を受け取り、クエリ結果を返します(ResultProxy-DB-APIカーソルオブジェクトをラップして行へのアクセスを容易にします列。)


さらに明確にするために(推奨されるアプローチではなく、概念を明確にするためだけに)

Engine.execute()(コネクションレス実行)、Connection.execute()、およびSession.execute()に加えて、execute()を任意のExecutableコンストラクトで直接使用することもできます。 Executableクラスにはexecute()の独自の実装があります-公式ドキュメントによると、execute()の機能に関する1行の説明は、「このExecutableをコンパイルして実行する」です。この場合、Executable(SQL式の構成体)をConnectionオブジェクトまたはEngineオブジェクト(暗黙的にConnectionオブジェクトを取得)に明示的にバインドする必要があるため、execute()SQLの実行場所を認識します。

次の例はそれをよく示しています-以下のような表を与えられます:

from sqlalchemy import MetaData, Table, Column, Integer

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

明示的な実行すなわちConnection.execute()-SQLテキストまたは構築されたSQL式をConnectionexecute()メソッドに渡す:

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

明示的なコネクションレス実行すなわちEngine.execute()-SQLテキストまたは構築されたSQL式をEngineのexecute()メソッドに直接渡す:

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

暗黙の実行すなわちExecutable.execute()-もコネクションレスであり、Executableexecute()メソッドを呼び出します。つまり、SQL式の構成で直接execute()メソッドを呼び出します( Executable)自体のインスタンス。

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

注:明確化のために暗黙の実行例を記載しました-この実行方法は、強くお勧めしません- docs に従って:

「暗黙の実行」は非常に古い使用パターンであり、ほとんどの場合、役立つよりも混乱を招き、その使用は推奨されません。どちらのパターンも、アプリケーションの設計における適切な「ショートカット」の過剰使用を助長しているようで、後で問題が発生します。


あなたの質問:

誰かがengine.executeを使用すると接続を作成し、セッションを開き(Alchemyがあなたのためにそれを気にする)、クエリを実行することを理解しています。

「誰かがengine.executeを使用する場合はconnectionを作成しますが、「sessionを開き(Alchemyはそれを気にかけます)、クエリを実行します」という部分は正しいです-Engine.execute()Connection.execute()の使用は(ほぼ)同じです正式には、Connectionオブジェクトは暗黙的に作成され、後のケースでは明示的にインスタンス化されます。この場合に実際に起こることは次のとおりです。

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

しかし、このようなタスクを実行するこれら3つの方法には、グローバルな違いがありますか?

DBレイヤーではまったく同じことで、すべてがSQL(テキスト式またはさまざまなSQL式の構成体)を実行しています。アプリケーションの観点からは、2つのオプションがあります。

  • 直接実行-Engine.execute()またはConnection.execute()を使用
  • sessionsの使用-トランザクションを単一の作業単位として効率的に処理し、session.add()session.rollback()session.commit()session.close()を介して簡単に処理します。 ORMの場合、つまりマップされたテーブルの場合、DBと対話する方法です。 identity_map を提供して、1回の要求で既にアクセス済みのオブジェクトまたは新しく作成/追加されたオブジェクトを即座に取得します。

Session.execute()は、最終的にSQLステートメントを実行するためにConnection.execute()ステートメント実行メソッドを使用します。 Sessionオブジェクトの使用は、アプリケーションがデータベースと対話するためのSQLAlchemy ORMの推奨方法です。

docs からの抜粋:

SQLAlchemy ORMを使用する場合、これらのオブジェクトは通常アクセスされないことに注意することが重要です。代わりに、Sessionオブジェクトがデータベースへのインターフェイスとして使用されます。ただし、ORMの上位レベルの管理サービスに関与せずにテキストSQLステートメントやSQL式の構成要素を直接使用するように構築されたアプリケーションの場合、エンジンと接続は重要です(そして、女王様ですか)。

92
Nabeel Ahmed

Nabeelの答え は多くの詳細をカバーし、役に立ちますが、従うのはわかりにくいと感じました。現在、これがこの問題に対する最初のGoogle結果であるため、この質問を見つけた将来の人々のために私の理解を追加します。

.execute()の実行

OPとNabell Ahmedの両方が指摘しているように、プレーンSELECT * FROM tablenameを実行すると、提供される結果に違いはありません。

これら3つのオブジェクトの違いは、SELECTステートメントが使用されるコンテキストに応じて、またはより一般的にはINSERTDELETEなどのような他のことをしたいときに重要になります。 。

一般的にエンジン、接続、セッションを使用する場合

  • Engineは、SQLAlchemyが使用する最低レベルのオブジェクトです。 It 接続のプールを維持する アプリケーションがデータベースと通信する必要があるときはいつでも使用できます。 .execute()は、最初にconn = engine.connect(close_with_result=True)を呼び出し、次にconn.execute()を呼び出す便利なメソッドです。 close_with_resultパラメーターは、接続が自動的に閉じられることを意味します。 (私はソースコードを少し言い換えていますが、本質的に本当です)。 編集: これはengine.executeのソースコードです

    エンジンを使用して生のSQLを実行できます。

    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()
    

    これについては、ドキュメントの 基本的な使用法 で説明されています。

  • Connectionは(上で見たように)実際にSQLクエリを実行する作業を行うものです。接続の属性をより細かく制御したいとき、閉じられたときなどにこれを行う必要があります。たとえば、これの非常に重要な例は Transaction で、いつコミットするかを決定できますデータベースへの変更。通常の使用では、変更は自動コミットされます。トランザクションを使用すると、(たとえば)いくつかの異なるSQLステートメントを実行でき、それらの1つで何か問題が発生した場合、すべての変更を一度に取り消すことができます。

    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
    

    これにより、データログテーブルの作成を忘れた場合など、一方が失敗した場合に両方の変更を元に戻すことができます。

    したがって、生のSQLコードを実行していて制御が必要な場合は、接続を使用します

  • Sessionsは、SQLAlchemyのオブジェクトリレーションシップ管理(ORM)の側面に使用されます(実際、インポート方法からこれを確認できます:from sqlalchemy.orm import sessionmaker)。内部で接続とトランザクションを使用して、自動生成されたSQLステートメントを実行します。 .execute()は、セッションのバインド先(通常はエンジンですが、接続でもかまいません)に渡す便利な関数です。

    ORM機能を使用している場合は、セッションを使用します。オブジェクトにバインドされていない単純なSQLクエリのみを実行する場合は、接続を直接使用することをお勧めします。

73
Neal

GRANTなどのDCL(データ制御言語)を実行する例を次に示します

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
0
Jie