J'utilise SQLAlchemy et il y a au moins trois entités: engine
, session
et connection
, qui ont la méthode execute
, donc si je p. Ex. veux sélectionner tous les enregistrements de table
je peux le faire
engine.execute(select([table])).fetchall()
et ça
connection.execute(select([table])).fetchall()
et même cela
session.execute(select([table])).fetchall()
- les résultats seront les mêmes.
Si je comprends bien, si quelqu'un utilise engine.execute
il crée connection
, ouvre session
(Alchemy s'en occupe pour vous) et exécute la requête. Mais existe-t-il une différence globale entre ces trois manières de réaliser une telle tâche?
Un aperçu d'une ligne:
Le comportement de execute()
est identique dans tous les cas, mais il existe 3 méthodes différentes, dans les classes Engine
, Connection
et Session
.
En quoi consiste exactement execute()
:
Pour comprendre le comportement de execute()
, nous devons examiner la classe Executable
. Executable
est une superclasse pour tous les types d’objets "instruction", y compris select (), delete (), update (), insert (), text () - en termes simples, un Executable
est une construction d'expression SQL prise en charge dans SQLAlchemy.
Dans tous les cas, la méthode execute()
prend le texte SQL ou l'expression SQL construite, c'est-à-dire n'importe laquelle des constructions d'expression SQL prises en charge dans SQLAlchemy et renvoie les résultats de la requête (un ResultProxy
- Enveloppe un DB-API
Objet curseur pour faciliter l’accès aux colonnes de lignes.)
Pour clarifier davantage (uniquement à des fins de clarification conceptuelle, ce n'est pas une approche recommandée) :
En plus de Engine.execute()
(exécution sans connexion), Connection.execute()
et Session.execute()
, il est également possible d'utiliser le execute()
directement sur tout Executable
construit. La classe Executable
a sa propre implémentation de execute()
- Selon la documentation officielle, une description de la fonction de la fonction execute()
est " Compilez et exécutez ce Executable
". Dans ce cas, nous devons explicitement lier la Executable
(construction d’expression SQL) à un objet Connection
ou à un objet Engine
(qui obtient implicitement un objet Connection
), donc la execute()
saura où exécuter le SQL
.
L'exemple suivant le montre bien - à partir d'un tableau comme ci-dessous:
from sqlalchemy import MetaData, Table, Column, Integer
meta = MetaData()
users_table = Table('users', meta,
Column('id', Integer, primary_key=True),
Column('name', String(50)))
Exécution explicite ie Connection.execute()
- passage du texte SQL ou de l'expression SQL construite à la méthode execute()
de Connection
:
engine = create_engine('sqlite:///file.db')
connection = engine.connect()
result = connection.execute(users_table.select())
for row in result:
# ....
connection.close()
Exécution explicite sans connexion ie Engine.execute()
- passage du texte SQL ou de l'expression SQL construite directement à la méthode execute()
de Moteur:
engine = create_engine('sqlite:///file.db')
result = engine.execute(users_table.select())
for row in result:
# ....
result.close()
Exécution implicite ie Executable.execute()
- est également sans connexion et appelle la méthode execute()
de la Executable
, c’est-à-dire qu’elle appelle la méthode execute()
directement sur la construction d’expression SQL
(une instance de Executable
) elle-même.
engine = create_engine('sqlite:///file.db')
meta.bind = engine
result = users_table.select().execute()
for row in result:
# ....
result.close()
Remarque: a indiqué l’exemple d’exécution implicite à des fins de clarification - ce mode d’exécution est vivement déconseillé - conformément à docs :
L '"exécution implicite" est un modèle d'utilisation très ancien qui, dans la plupart des cas, crée plus de confusion qu'il n'est utile, et son utilisation est découragée. Les deux modèles semblent encourager l'utilisation excessive de "raccourcis" opportuns dans la conception des applications, ce qui entraîne des problèmes ultérieurement.
Si je comprends bien, si quelqu'un utilise engine.execute, il crée une connexion, ouvre une session (Alchemy s'en occupe pour vous) et exécute une requête.
Vous avez raison pour la partie "si quelqu'un utilise engine.execute
, Il crée connection
" mais pas pour "ouvre session
(Alchemy s'en soucie pour vous) et exécute la requête" - Utiliser Engine.execute()
et Connection.execute()
est (presque) la même chose, en formel, l'objet Connection
est créé implicitement et, dans les cas ultérieurs, nous l'instancions explicitement. Ce qui se passe réellement dans ce cas c'est:
`Engine` object (instantiated via `create_engine()`) -> `Connection` object (instantiated via `engine_instance.connect()`) -> `connection.execute({*SQL expression*})`
Mais existe-t-il une différence globale entre ces trois manières de réaliser une telle tâche?
Au niveau de la couche DB, c'est exactement la même chose, tous exécutent SQL (expression de texte ou diverses constructions d'expression SQL). Du point de vue de l'application, il y a deux options:
Engine.execute()
ou Connection.execute()
sessions
- traite efficacement les transactions comme une seule unité de travail, facilement via session.add()
, session.rollback()
, session.commit()
, session.close()
. C'est le moyen d'interagir avec la base de données dans le cas d'ORM, c'est-à-dire des tables mappées. Fournit identity_map pour obtenir instantanément les objets déjà consultés ou nouvellement créés/ajoutés au cours d'une seule demande.Session.execute()
utilise finalement la méthode d'exécution de l'instruction Connection.execute()
afin d'exécuter l'instruction SQL. L'utilisation de l'objet Session
constitue la méthode recommandée par SQLAlchemy ORM pour une application afin d'interagir avec la base de données.
Un extrait du docs :
Il est important de noter que lors de l'utilisation de SQLAlchemy ORM, ces objets ne sont généralement pas utilisés. à la place, l'objet Session est utilisé comme interface avec la base de données. Cependant, pour les applications construites autour de l'utilisation directe d'instructions SQL textuelles et/ou de constructions d'expression SQL sans implication des services de gestion de niveau supérieur de l'ORM, Engine et Connection sont roi (et reine?) - à lire.
La réponse de Nabeel couvre beaucoup de détails et est utile, mais j'ai trouvé cela déroutant à suivre. Dans la mesure où il s’agit actuellement du premier résultat Google relatif à ce problème, j’ajoute ce que je comprends aux futurs utilisateurs qui trouveront cette question:
Comme OP et Nabell Ahmed notent tous les deux, lors de l'exécution d'un simple SELECT * FROM tablename
, Il n'y a aucune différence dans le résultat fourni.
Les différences entre ces trois objets deviennent importantes en fonction du contexte dans lequel l'instruction SELECT
est utilisée ou, plus généralement, lorsque vous souhaitez effectuer d'autres tâches telles que INSERT
, DELETE
, etc.
Le moteur est l'objet de niveau le plus bas utilisé par SQLAlchemy. Il maintient un pool de connexions utilisable chaque fois que l’application doit communiquer avec la base de données. .execute()
est une méthode pratique qui appelle d'abord conn = engine.connect(close_with_result=True)
puis conn.execute()
. Le paramètre close_with_result signifie que la connexion est fermée automatiquement. (Je paraphrase légèrement le code source, mais essentiellement vrai). edit: Voici le code source de engine.execute
Vous pouvez utiliser le moteur pour exécuter du SQL brut.
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()
Ceci est couvert dans la documentation sous tilisation de base .
Connection est (comme nous l'avons vu ci-dessus) la chose qui exécute réellement le travail d'exécution d'une requête SQL. Vous devriez le faire chaque fois que vous souhaitez contrôler davantage les attributs de la connexion, la fermer, etc. Par exemple, un exemple très important de cela est un Transaction , qui vous permet de décider quand modifications à la base de données. En utilisation normale, les modifications sont auto-validées. Avec l'utilisation de transactions, vous pouvez (par exemple) exécuter plusieurs instructions SQL différentes et, en cas de problème, vous pouvez annuler toutes les modifications en même temps.
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
Cela vous permettrait d'annuler les deux modifications si l'une d'elles échouait, comme si vous aviez oublié de créer la table d'enregistrement des données.
Donc, si vous exécutez du code SQL brut et avez besoin de contrôle, utilisez des connexions
Les sessions sont utilisées pour l'aspect ORM (Object Relationship Management) de SQLAlchemy (en fait, vous pouvez voir cela de la manière dont elles ont été importées: from sqlalchemy.orm import sessionmaker
). Ils utilisent des connexions et des transactions sous le capot pour exécuter leurs instructions SQL générées automatiquement. .execute()
est une fonction pratique qui transmet à tout ce à quoi la session est liée (généralement un moteur, mais peut être une connexion).
Si vous utilisez la fonctionnalité ORM, utilisez session. Si vous ne faites que des requêtes SQL directes non liées à des objets, il est probablement préférable d'utiliser directement des connexions.
Voici un exemple d’exécution de DCL (langage de contrôle de données) tel que 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