SqlAlchemyでSQLサーバーのストアドプロシージャを呼び出すにはどうすればよいですか?
エンジンと接続には、任意のSQLステートメントに使用できるexecute()
メソッドがあり、セッションも同様です。例えば:
_results = sess.execute('myproc ?, ?', [param1, param2])
_
必要に応じて、outparam()
を使用して出力パラメーターを作成できます(またはバインドパラメーターの場合は、_isoutparam=True
_オプションを指定したbindparam()
を使用します)
context:MySQLでORMマッピングなしでフラスコsqlalchemyを使用します。通常、私は使用します:
_# in the init method
_db = SqlAlchemy(app)
#... somewhere in my code ...
_db.session.execute(query)
_
ストアドプロシージャの呼び出しはそのままではサポートされていません。callproc
は汎用ではなく、mysqlコネクタに固有です。
ストアドプロシージャout paramsなしの場合、次のようなクエリを実行できます。
__db.session.execute(sqlalchemy.text("CALL my_proc(:param)"), param='something')
_
いつものように。 out params ...を使用すると、状況はさらに複雑になります。
Out paramsを使用する1つの方法は、基になるコネクタにアクセスすることで、engine.raw_connection()
を使用します。例えば:
_conn = _db.engine.raw_connection()
# do the call. The actual parameter does not matter, could be ['lala'] as well
results = conn.cursor().callproc('my_proc_with_one_out_param', [0])
conn.close() # commit
print(results) # will print (<out param result>)
_
Outパラメータにアクセスできるので、これは素晴らしいですしかし、この接続はflask sessionによって管理されていません。これは、コミットされないことを意味します/他のマネージクエリと同様に中止されます...(プロシージャに副作用がある場合のみ問題があります)。
最後に、私はこれをやった:
_# do the call and store the result in a local mysql variabl
# the name does not matter, as long as it is prefixed by @
_db.session.execute('CALL my_proc_with_one_out_param(@out)')
# do another query to get back the result
result = _db.session.execute('SELECT @out').fetchone()
_
result
は、1つの値(outパラメータ)を持つタプルになります。これは理想的ではありませんが、最も危険ではありません。セッション中に別のクエリが失敗した場合、プロシージャコールも中止されます(ロールバック)。
func
で作成されたプロシージャオブジェクトを実行するだけです。
from sqlalchemy import create_engine, func
from sqlalchemy.orm import sessionmaker
engine = create_engine('sqlite://', echo=True)
print engine.execute(func.upper('abc')).scalar() # Using engine
session = sessionmaker(bind=engine)()
print session.execute(func.upper('abc')).scalar() # Using session
すでにsessionmaker()で作成したセッションがあるとすると、次の関数を使用できます。
def exec_procedure(session, proc_name, params):
sql_params = ",".join(["@{0}={1}".format(name, value) for name, value in params.items()])
sql_string = """
DECLARE @return_value int;
EXEC @return_value = [dbo].[{proc_name}] {params};
SELECT 'Return Value' = @return_value;
""".format(proc_name=proc_name, params=sql_params)
return session.execute(sql_string).fetchall()
これで、次のようなパラメーターを使用して、ストアドプロシージャ 'MyProc'を実行できます。
params = {
'Foo': foo_value,
'Bar': bar_value
}
exec_procedure(session, 'MyProc', params)
SQLAlchemyを使用してMySQLでストアドプロシージャを呼び出す最も簡単な方法は、Engine.raw_connection()
のcallproc
メソッドを使用することです。 call_proc
には、呼び出されるストアドプロシージャに必要なプロシージャ名とパラメータが必要です。
def call_procedure(function_name, params):
connection = cloudsql.Engine.raw_connection()
try:
cursor = connection.cursor()
cursor.callproc(function_name, params)
results = list(cursor.fetchall())
cursor.close()
connection.commit()
return results
finally:
connection.close()
私のプロジェクトの必死の必要性から、私はストアドプロシージャコールを処理する関数を書きました。
どうぞ:
import sqlalchemy as sql
def execute_db_store_procedure(database, types, sql_store_procedure, *sp_args):
""" Execute the store procedure and return the response table.
Attention: No injection checking!!!
Does work with the CALL syntax as of yet (TODO: other databases).
Attributes:
database -- the database
types -- Tuple of strings of SQLAlchemy type names.
Each type describes the type of the argument
with the same number.
List: http://docs.sqlalchemy.org/en/rel_0_7/core/types.html
sql_store_procudure -- string of the stored procedure to be executed
sp_args -- arguments passed to the stored procedure
"""
if not len(types) == len(sp_args):
raise ValueError("types Tuple must be the length of the sp args.")
# Construch the type list for the given types
# See
# http://docs.sqlalchemy.org/en/latest/core/sqlelement.html?highlight=expression.text#sqlalchemy.sql.expression.text
# sp_args (and their types) are numbered from 0 to len(sp_args)-1
type_list = [sql.sql.expression.bindparam(
str(no), type_=getattr(sql.types, typ)())
for no, typ in Zip(range(len(types)), types)]
try:
# Adapts to the number of arguments given to the function
sp_call = sql.text("CALL `%s`(%s)" % (
sql_store_procedure,
", ".join([":%s" % n for n in range(len(sp_args))])),
bindparams=type_list
)
#raise ValueError("%s\n%s" % (sp_call, type_list))
with database.engine.begin() as connection:
return connection.execute(
sp_call,
# Don't do this at home, kids...
**dict((str(no), arg)
for (no, arg) in Zip(range(len(sp_args)), sp_args)))
except sql.exc.DatabaseError:
raise
これはCALL構文で動作するため、MySQLは期待どおりに動作するはずです。 MSSQLは呼び出しの代わりにEXECを使用し、構文が少し異なります。したがって、サーバーにとらわれないようにするのはあなた次第ですが、それほど難しくありません。
別の回避策:
query = 'call Procedure ('+"'"+@param1+"'"+','+"'"+@param2+"'"+','+"'"+@param3+"'"+')'
sqlEngine = sqlalchemy.create_engine(jdbc)
conn = sqlEngine.connect()
df = pd.read_sql(query,conn,index_col=None)