2013-07-31 55 views
110

如何在SQLAlchemy中執行原始SQL?如何在SQLAlchemy-flask應用程序中執行原始SQL

我有一個python web應用程序,它通過SQLAlchemy在燒瓶上運行,並與數據庫接口。

我需要一種方法來運行原始SQL。該查詢涉及多個表連接以及嵌入視圖。

我已經試過:

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

但我不斷收到網關錯誤。

+1

我建議你只使用http://pythonhosted.org/Flask-SQLAlchemy/也很快忘記了執行原始的SQL查詢。 –

+2

我已經看過,但我找不到有關運行更新的教程。我寧願不學習語法,也不想學習相當長的(約20行)SQL查詢。 – starwing123

+53

@MarkusUnterwaditzer我曾經認爲,但現在我強烈反對。原始的,適當參數化的SQL通常比一堆產生它的函數調用和對象更易於讀取和維護。它還爲您提供數據庫的全部功能,而無需跳過這些環節,使ORM生成正確的語法(如果甚至可能的話),並使ORM​​不會做出意想不到的事情。您可能會問這個問題:「那麼爲什麼要使用SQLAlchemy?」,而我唯一的答案是,「現有的應用程序使用它並改變所有內容太昂貴。」 – jpmc26

回答

171

你試過:

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

或:

from sqlalchemy import text 

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

print names 
+2

如果你做了一個插入或更新,你如何提交交易? –

+9

如果您使用的是原始SQL,那麼您可以控制事務,因此您必須自己發佈'BEGIN'和'COMMIT'語句。 – Miguel

+0

我試過了,它似乎不適合我。非常好奇。 –

0

您是否嘗試過使用connection.execute(text(<sql here>), <bind params here>)和綁定參數描述in the docs?這可以幫助解決許多參數格式和性能問題。也許網關錯誤是超時?綁定參數傾向於使複雜的查詢執行速度大幅提高。

+2

根據[文檔](http://docs.sqlalchemy.org/en/rel_0_8/core/tutorial.html#using-text),它應該是'connection.execute(文本(),)'。 'bind params'不應該在'text()'中。 _feeding的綁定參數執行()method_ –

+0

是的,你當然是。我修正了這個,謝謝。 – jhnwsk

41

文檔:SQL Expression Language Tutorial - Using Text

例如:

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, u'Tim', u'Gurra', u'Staff', u'991-509-9284') 
(1, u'Jim', u'Carey', u'Staff', u'832-252-1910') 
(2, u'Lee', u'Asher', u'Staff', u'897-747-1564') 
(3, u'Ben', u'Hayes', u'Staff', u'584-255-2631') 
+1

到sqlalchemy文檔的鏈接似乎已過時。這是更近的:http://docs.sqlalchemy.org/en/latest/core/tutorial.html#using-textual-sql – Carl

+0

我可以問爲什麼我們使用'=='? –

+1

@Jake Berger非常感謝您。我已經浪費了將近一天的時間來尋找這個答案。我只是直接執行SQL而不轉換爲文本。每當我的where子句中有%students%時,就會發生錯誤。對你的回答表示熱烈的掌聲。 –

84

如果你想使用一個會話(如你的問題建議),利用其execute方法直接:

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

下面的威力特定於我的數據庫驅動程序(psycopg2);我不確定。無論如何,這是我如何拉出我的價值觀。

from collections import namedtuple 

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

關鍵是撥打fetchall()namedtuple部分只是我發現通過提供基於名稱的訪問使我的生活更輕鬆。

此外,這是交易沒有手動管理它。說make_session是創建一個會話的功能:

>>> s1 = make_session() 
>>> s1.execute('CREATE TABLE blah (id INTEGER)') 
<sqlalchemy.engine.result.ResultProxy object at 0x02CD86F0> 
>>> s1.commit() 
>>> 
>>> s1.execute('INSERT INTO blah VALUES (1)') 
<sqlalchemy.engine.result.ResultProxy object at 0x02CD8870> 
>>> s1.execute('SELECT * FROM blah').fetchall() 
[(1,)] 
>>> 
>>> s2 = make_session() 
>>> s2.execute('SELECT * FROM blah').fetchall() 
[] 
>>> s2.close() 
>>> 
>>> s1.commit() 
>>> 
>>> s2 = make_session() 
>>> s2.execute('SELECT * FROM blah').fetchall() 
[(1,)] 
>>> s2.close() 
>>> s1.close() 
19

你可以SELECT SQL的查詢結果使用from_statement()text()如圖所示here。你不必以這種方式處理t ules。對於具有表名「用戶」,你可以嘗試一類用戶爲例,

from sqlalchemy.sql import text 
. 
. 
. 
user = session.query(User).from_statement(
    text("SELECT * FROM users where name=:name")).\ 
    params(name='ed').all() 

return user 
6
result = db.engine.execute(text("<sql here>")) 

執行<sql here>但除非你在autocommit模式不提交。所以,插入和更新不會反映在數據庫中。

要更改後提交,做

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