2

大規模な PostgreSQL テーブルに対して次のコードを実行すると、すべてのデータがフェッチされるまで 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にプリフェッチを強制する」ものはありません。なぜこれが起こっているのか、私は完全に途方に暮れています。

4

2 に答える 2

3

I'm surprised the driver doesn't provide a way to do this-- but you could manually execute the SQL statements to declare a cursor, open it and fetch from it in batches. i.e. (and this code is very dubious as I'm not a C# guy):

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();

Note that:

  • You need to be inside a transaction block to use a cursor, unless you specify the "HOLD" option when declaring it, in which case the server will spool the results to a server-side temp file (you just won't have to transfer it all at once though)
  • The cursor_tuple_fraction setting may cause a different plan to be used when executing a query via a cursor as opposed to in immediate mode. You may want to do "SET cursor_tuple_fraction=1" just before declaring the cursor since you're actually intending to fetch all the cursor's output.
于 2010-06-06T12:27:13.680 に答える
1

どの Npgsql バージョンを使用していますか? 少し前に大きなテーブルのサポートを追加しました。実際、Postgresql プロトコル バージョン 3 は、カーソルを使用せずに大きな結果セットをページングすることをサポートしています。残念ながら、まだ実装していません。そのために残念。

Npgsql 2.0.9 で試してみて、まだ問題がある場合はお知らせください。

于 2010-06-16T14:32:46.040 に答える