2010-06-06 146 views
2

對大型PostgreSQL表運行以下代碼時,NpgsqlDataReader對象會阻塞,直到抓取所有數據。如何阻止NpgsqlDataReader阻塞?

NpgsqlCommand cmd = new NpgsqlCommand(strQuery, _conn); 
NpgsqlDataReader reader = cmd.ExecuteReader(); // <-- takes 30 seconds 

我怎樣才能讓它表現得不會預取所有的數據?我想逐行瀏覽結果集,而不是一次將所有15 GB的內容全部讀取到內存中。

我知道在Npgsql 1.x中存在這類問題,但我在2.0上。這是針對XP/Vista/7上的PostgreSQL 8.3數據庫。我也沒有任何時髦的「強制Npgsql預取」我的連接字符串中的東西。我完全喪失了爲什麼會發生這種情況。

回答

3

我很驚訝驅動程序沒有提供這樣做的方法 - 但是您可以手動執行SQL語句來聲明遊標,打開並批量獲取它。即(這個代碼是非常可疑的,因爲我不是一個C#的傢伙):

new PgsqlCommand("DECLARE cur_data NO SCROLL CURSOR AS " 
       + strQuery, _conn).ExecuteNonQuery(); 
do { 
    NpgsqlDataReader reader = new NpgsqlCommand("FETCH 100 FROM cur_data", _conn) 
              .ExecuteReader(); 
    int rows = 0; 
    // read data from reader, incrementing "rows" for each row 
} while (rows > 0); 
new PgsqlCommand("CLOSE cur_data", _conn).ExecuteNonQuery(); 

需要注意的是:

  • 你需要一個事務塊裏面使用遊標,除非您指定在聲明它時「HOLD」選項,在這種情況下,服務器會將結果後臺打印到服務器端臨時文件中(您不必一次將它全部傳輸)
  • cursor_tuple_fraction設置可能會導致不同計劃在通過遊標執行查詢時使用,而不是在即時模式下。您可能希望在聲明遊標之前執行「SET cursor_tuple_fraction = 1」,因爲您實際上是想要獲取所有遊標的輸出。
+0

我會upvote,但我沒有聲望呢。我的備份計劃是按照您的建議手動聲明遊標。理想情況下,我希望DataReader「只是工作」。 :)是不是DataReader的全部重點,它是一種快速訪問數據的方式?所以這種行爲對我來說似乎很陌生。 – 2010-06-06 14:36:05

+0

@Swingline Rage:.net驅動程序甚至可能不如JDBC驅動程序成熟。我相信,postgresql協議有一種機制,用於使用遊標獲取任何查詢的結果(SQL命令是一個單獨的更高級別的接口) - 也許Npgsql根本不支持使用寫入的內容? Istr傳統的PQexecutequery()調用實際上只是一次返​​回整個結果,並且可能試圖將其保留在OS緩衝區中,並且處理它的peicemeal會很糟糕... – araqnid 2010-06-06 16:06:06

+0

是的好點(並且有點令人沮喪)。我會更詳細地研究這一點。我一直在忘記Npgsql是開源的,所以答案就在那裏。但是最後一次看,我發現代碼很難遵循,就像數據內部一樣。 – 2010-06-09 16:13:09

1

您正在使用哪種Npgsql版本?我們前一段時間增加了對大型表格的支持。實際上,Postgresql協議版本3支持通過大型結果集而不使用遊標進行分頁。不幸的是我們還沒有實現它。對不起。

請用Npgsql 2.0.9試試看,如果你還有問題,請告訴我。