2013-04-11 58 views
1

我正在寫一個查詢數據庫結果的小程序(單個表)。我使用python 3.3,sqlalchemy和postgres數據庫。從數據庫中逐一獲取結果

result = db_session.query(Data).all() 
progress = 0 
for row in result: 
    update_progress_bar(progress, len(result)) 
    do_something_with_data(row) 
    progress += 1 

變量'result'將包含數千行,並且處理數據需要一些時間。這就是爲什麼我介紹了一個簡單的進度條,以瞭解它將花費多少時間。 問題是,總時間的30%是對數據庫進行排隊(第一行)。所以當我開始程序時,我的進度條開始移動之前會有很大的延遲。另外,我不需要在記憶中保留所有結果。我可以分別處理它們。

有沒有什麼辦法可以修改上面的程序來逐個獲取行,直到接收到所有行,而不需要將所有內容都加載到內存中?另外我想監視查詢和處理數據的進度。

+0

感謝EOL消解,我看看「流式結果」。爲了讓它起作用,我必須使用「.execution_options(stream_results = True)」運行查詢。我還發現我的數據庫適配器(pypostgresql)不支持流式傳輸。唯一能夠做到這一點的是psycop2。幸運的是,新的psycop2 2.5現在支持python 3.3 :)我將測試並分享經驗:) – Marek 2013-04-11 12:47:47

回答

3

你只是需要在查詢循環不調用.all()呼叫.yield_per()設置批量大小:

for row in db_session.query(Data).yield_per(10): 
    do_something_with_data(row) 

.all()確實變成了整個結果集到一個列表第一,如果結果集很大,會導致延遲。在設置.yield_per()之後直接重複查詢查詢,而是根據需要提取結果,只要數據庫API支持即可。

如果你想知道的前期有多少行會被退回,打電話.count()第一:

result = db_session.query(Data) 
count = result.count() 

for row in result.yield_per(10): 
    update_progress_bar(progress, count) 
    do_something_with_data(row) 
    progress += 1 

.count()詢問數據庫來給我們一個項目計數爲我們第一。

您的數據庫可能仍會預先緩存結果行,從而導致啓動延遲,即使使用.yield_per()。在這種情況下,您需要使用windowed query根據列中一個中的值範圍將查詢分解爲塊。無論是否這將取決於你的確切的表格佈局。

+1

您是否有參考需要的迭代器獲取結果?如果我沒有記錯,沒有多少DBAPI支持流式處理,而Sqlite3尤其會抓取所有的行。 – EOL 2013-04-11 12:20:43

+0

不幸的是我仍然在程序進入主循環之前得到延遲。我忘了提及,我正在使用PostgreSQL。 – Marek 2013-04-11 12:23:04

+0

@Marek:數據庫*本身*可以很容易地需要一些啓動時間,然後才能開始提供數據。也可能是數據庫API本身不支持流式傳輸。使用'.all()'*保證*你首先將所有內容加載到內存中,遍歷查詢至少會給底層數據庫API提供*機會*給你分批結果。 – 2013-04-11 12:45:38