1

SqlDataReader を使用してデータを読み取るメソッドがあり、yield は IEnumerable を返します。

IEnumerable<string> LoadCustomers()
{
 using(SqlDataReader rdr = cmd.ExecuteReader())
 {
    while (rdr.Read())
    {
        yield return rdr.GetString(0); 
    }
 }
}

ここで、最新の 10 人の顧客のみが必要であると仮定しましょう。私はそれをできた

LoadCustomers.Take(10)

または 10 をパラメーターとして sql に渡し、my sql を作成します

SELECT TOP 10 FROM Customers ORDER BY CreationDate DESC

この投稿によると、datareaderが数行しか読み取らない場合でも(接続が開いている限り)、結果セット全体がSQLサーバーからクライアントに送信されています-Take(10)余分なデータがとにかくクライアントに送信されるため、アプローチを避ける必要がありますまたは、それを回避するのは時期尚早の最適化になるでしょうか (yield リターン コードが 10 行を読み取った後に接続を閉じ、データ転送がとにかく停止するため)?

4

2 に答える 2

2

最適化が「時期尚早」であるかどうかは主観的なものであるため、この質問を「a を使用して10 行後に読み取りを停止することは、クエリでDataReader使用する場合と同じパフォーマンス特性を持っているか?」と解釈することにしました。TOP(10)

答えはノーだ。TOP(10)サーバーに渡すことで、オプティマイザーは、クエリが最大 10 行を返す (この場合は読み取りも行う) ことを認識して、読み取り、メモリ許可、I/O バッファー、ロックの粒度、および並列処理を調整できます。TOPクライアントがすべての行を読み取ろうとしている場合に備えなければならない手段を除外します - 実際に早く停止するかどうかに関係なく。

行を読み取るかどうかに関係なく、サーバーが行を送信するというのは正しくありません。を使用して行をプルすることSqlDataReaderは、概念的には行ごとの操作です。 を発行Reader.MoveNextすると、サーバーから次の行がフェッチされ、その行のみがフェッチされます。ただし、パフォーマンスのために、行は要求する前にバッファリングされます (サーバー側とネットワーク バッファ側の両方)。.MoveNextそのため、最初の呼び出しの後で、10 行しか読み取れなかったとしても、最終的に 100 行がバッファーに取得される可能性があります。

オーバーヘッドに関しては、これらのバッファーのサイズは最終的に固定されているため、これは私の主な関心事ではありません。サーバーは、結果セットの数に関係なく、結果セットのすべての行をバッファリングしません (これは一般的に非常に非効率的です)。10 行しか読み取らない場合、クエリが最後まで実行された場合に最終的に 1,000 行または 1,000,000 行を返すかどうかは、バッファリングに関しては問題ではなく、主にクエリ プランに関して重要です。それにもかかわらず、オーバーヘッドが追加されます。

于 2016-07-29T10:48:59.120 に答える
1

ページネーション Skip(0) と Take(10) をより柔軟に使用することもできます。

SQL サーバー 2012

SELECT name,
       CreationDate        
  FROM customer
 ORDER BY
       CreationDate      
OFFSET @skip ROWS
FETCH NEXT @take ROWS ONLY;

SQL 2005 ~ 2008

SET @take = (@skip + @take)

;WITH customer_page_cte AS
(SELECT 
        name, 
        CreationDate,
        ROW_NUMBER() OVER (ORDER BY CreationDate desc) AS RowNumber
 FROM customer
)

SELECT name, 
       CreationDate
  FROM customer_page_cte
 WHERE RowNumber > @skip AND RowNumber <= @take

C# with sql 2012 - コマンドにストアド プロシージャを使用します:)

var command = @"SELECT name,
                       CreationDate        
                  FROM customer
                 ORDER BY
                       CreationDate      
                 OFFSET @skip ROWS
                 FETCH NEXT @take ROWS ONLY;";

            using (var conn = new SqlConnection("Data Source=.;Initial Catalog=Stackoverflow;Integrated Security=True"))
            {
                conn.Open();
                using (var cmd = new SqlCommand(command, conn))
                {
                    cmd.Parameters.AddWithValue("skip", 0);
                    cmd.Parameters.AddWithValue("take", 10);

                    var reader = cmd.ExecuteReader();
                    while (reader.Read())
                    {
                        Console.WriteLine(reader.GetString(0));
                    }
                }
            }
于 2016-07-27T10:09:57.800 に答える