284

你如何在 SQLAlchemy 中执行原始 SQL?

我有一个在烧瓶上运行的 python Web 应用程序,并通过 SQLAlchemy 连接到数据库。

我需要一种运行原始 SQL 的方法。该查询涉及多个表连接以及内联视图。

我试过了:

connection = db.session.connection()
connection.execute( <sql here> )

但我不断收到网关错误。

4

9 回答 9

376

你有没有尝试过:

result = db.engine.execute("<sql here>")

或者:

from sqlalchemy import text

sql = text('select name from penguins')
result = db.engine.execute(sql)
names = [row[0] for row in result]
print names

请注意,这db.engine.execute()是“无连接”,在 SQLAlchemy 2.0 中已弃用

于 2013-08-01T07:32:51.480 回答
243

SQL Alchemy 会话对象有自己的execute方法:

result = db.session.execute('SELECT * FROM my_table WHERE my_column = :val', {'val': 5})

您的所有应用程序查询都应该通过会话对象,无论它们是否是原始 SQL。这可确保查询由事务正确管理,从而允许将同一请求中的多个查询作为一个单元提交或回滚。使用引擎连接离开事务会使您面临更大的风险,即可能难以检测到可能导致数据损坏的错误。每个请求应该只与一个事务相关联,使用db.session将确保您的应用程序就是这种情况。

另请注意,它execute是为参数化查询而设计的。使用:val示例中的参数作为查询的任何输入,以保护自己免受 SQL 注入攻击。您可以通过将 a 作为第二个参数传递来为这些参数提供值dict,其中每个键是参数的名称,因为它出现在查询中。参数本身的确切语法可能因您的数据库而异,但所有主要的关系数据库都以某种形式支持它们。

假设它是一个SELECT查询,这将返回一个可迭代RowProxy对象。

您可以使用多种技术访问各个列:

for r in result:
    print(r[0]) # Access by positional index
    print(r['my_column']) # Access by column name as a string
    r_dict = dict(r.items()) # convert to dict keyed by column names

就个人而言,我更喜欢将结果转换为namedtuples:

from collections import namedtuple

Record = namedtuple('Record', result.keys())
records = [Record(*r) for r in result.fetchall()]
for r in records:
    print(r.my_column)
    print(r)

如果您不使用 Flask-SQLAlchemy 扩展,您仍然可以轻松地使用会话:

import sqlalchemy
from sqlalchemy.orm import sessionmaker, scoped_session

engine = sqlalchemy.create_engine('my connection string')
Session = scoped_session(sessionmaker(bind=engine))

s = Session()
result = s.execute('SELECT * FROM my_table WHERE my_column = :val', {'val': 5})
于 2014-02-28T01:56:51.210 回答
64

文档:SQL 表达式语言教程 - 使用文本

例子:

from sqlalchemy.sql import text

connection = engine.connect()

# recommended
cmd = 'select * from Employees where EmployeeGroup = :group'
employeeGroup = 'Staff'
employees = connection.execute(text(cmd), group = employeeGroup)

# or - wee more difficult to interpret the command
employeeGroup = 'Staff'
employees = connection.execute(
                  text('select * from Employees where EmployeeGroup = :group'), 
                  group = employeeGroup)

# or - notice the requirement to quote 'Staff'
employees = connection.execute(
                  text("select * from Employees where EmployeeGroup = 'Staff'"))


for employee in employees: logger.debug(employee)
# output
(0, 'Tim', 'Gurra', 'Staff', '991-509-9284')
(1, 'Jim', 'Carey', 'Staff', '832-252-1910')
(2, 'Lee', 'Asher', 'Staff', '897-747-1564')
(3, 'Ben', 'Hayes', 'Staff', '584-255-2631')
于 2013-09-15T04:35:32.110 回答
57

您可以使用from_statement()和获取 SELECT SQL 查询的结果text(),如下所示。您不必以这种方式处理元组。作为具有表名的类的示例,您可以尝试,Userusers

from sqlalchemy.sql import text

user = session.query(User).from_statement(
    text("""SELECT * FROM users where name=:name""")
).params(name="ed").all()

return user
于 2014-11-03T12:39:44.250 回答
14
result = db.engine.execute(text("<sql here>"))

执行<sql here>但不提交它,除非您处于autocommit模式。因此,插入和更新不会反映在数据库中。

要在更改后提交,请执行

result = db.engine.execute(text("<sql here>").execution_options(autocommit=True))
于 2015-09-03T07:03:12.423 回答
13

对于 SQLAlchemy ≥ 1.4

从 SQLAlchemy 1.4 开始,无连接或隐式执行已被弃用,即

db.engine.execute(...) # DEPRECATED

以及作为查询的裸字符串。

新的 API 需要显式连接,例如

from sqlalchemy import text

with db.engine.connect() as connection:
    result = connection.execute(text("SELECT * FROM ..."))
    for row in result:
        # ...

同样,如果有可用的会话,鼓励使用现有会话:

result = session.execute(sqlalchemy.text("SELECT * FROM ..."))

或使用参数:

session.execute(sqlalchemy.text("SELECT * FROM a_table WHERE a_column = :val"),
                {'val': 5})

有关详细信息,请参阅文档中的“无连接执行,隐式执行”。

于 2021-05-06T14:44:20.483 回答
2

这是关于如何从 Flask Shell 运行 SQL 查询的简化答案

首先,映射您的模块(如果您的模块/应用程序是主文件夹中的 manage.py 并且您在 UNIX 操作系统中),运行:

export FLASK_APP=manage

运行烧瓶外壳

flask shell

导入我们需要的东西::

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy(app)
from sqlalchemy import text

运行您的查询:

result = db.engine.execute(text("<sql here>").execution_options(autocommit=True))

这使用具有应用程序的当前数据库连接。

于 2019-01-26T05:46:37.417 回答
0

您是否尝试过按照文档中的说明connection.execute(text( <sql here> ), <bind params here> )使用和绑定参数?这可以帮助解决许多参数格式和性能问题。也许网关错误是超时?绑定参数往往会使复杂查询的执行速度大大加快。

于 2013-08-01T13:57:39.663 回答
-1

如果你想避免元组,另一种方法是调用first,oneall方法:

query = db.engine.execute("SELECT * FROM blogs "
                           "WHERE id = 1 ")

assert query.first().name == "Welcome to my blog"
于 2019-12-25T12:01:42.090 回答