2016-07-27 7 views
1

나는 SqlDataReader 개체를 사용하여 데이터를 읽고 수율은 IEnumerable을 반환하는 방법, 예컨대 :Take (10) vs TOP 10 SqlDataReader를 사용 하시겠습니까?

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

의 난은 최신 (10 명) 고객이 필요로 가정 할 수 있습니다. 나는 this post에 전체 결과 집합을 따라하는 것은 DataReader를 읽는 경우에도 클라이언트에 SQL 서버로부터 전송되는

SELECT TOP 10 FROM Customers ORDER BY CreationDate DESC 

내 SQL

LoadCustomers.Take(10) 

을하거나 SQL 매개 변수로 (10)를 통과 할 수있다 단 몇 행 (연결이 열려있는 한) - 어쨌든 클라이언트에 전송되는 여분의 데이터 또는이를 피하기위한 조기 최적화로 인해 Take(10) 접근 방식을 피해야합니까? (yield 리턴 코드가 그것은 10 행을 읽고 데이터 전송은 어쨌든 멈출 것입니까?)

+2

'주문서 작성일 DESC'? – jarlh

+4

너무 조숙 한 최적화가 아닐 것입니다. 실제로 필요한 것만 데이터베이스에서 가져옵니다. 10만이 필요할 때 10,000 명의 고객을 선택하는 것은 의미가 없습니다. –

+1

당신은 그 게시물이 말하는 것을 오해하고 있습니다. 일부 행은 이미 버퍼링되어있을 수 있지만 읽기를 중지하면 전체 결과 집합이 클라이언트에 전송되지 않습니다. 'SqlDataReader'는 네트워크를 넘어서는 "미리 읽기"를하지 않습니다. 대부분의 경우 데이터베이스 서버에'TOP (10) '을 보내려는 이유와 조기 최적화가 아닌 이유는 최적화 도구가 10 행만을 필요로한다는 것을 안다면 더 효과적인 계획을 세울 수 있기 때문입니다 전체 테이블을 읽습니다 (다른 것이 없으면 쿼리는 미리 적은 메모리를 할당합니다). –

답변

2

에 대한 사용 저장 프로 시저 "조기"는 주관적이며, 나는이 질문을 "DataReader 10 행이 쿼리에서 TOP(10)을 사용하는 것과 동일한 성능 특성을 갖는 후 읽기를 중지하겠습니까? "

대답은 '아니오'입니다. TOP(10)을 서버에 전달하면 옵티마이 저가 읽기, 메모리 부여, I/O 버퍼, 잠금 단위 및 병렬 처리를 튜닝 할 수 있습니다. 쿼리가 반환하는 정보 (이 경우 읽기도 가능)는 최대 10 개 행입니다. TOP을 제외하면 클라이언트가 실제로 이전에 중지했는지 여부에 관계없이 클라이언트가 모든 행을 읽는 경우를 대비해야합니다.

서버가 읽었는지 여부에 관계없이 서버가 행을 전송한다는 것은 사실이 아닙니다. SqlDataReader 행을 가져 오는 것은 개념적으로 행 단위 작업입니다. Reader.MoveNext을 실행하면 서버에서 다음 행을 가져오고 그 행만 가져옵니다. 그러나 성능 향상을 위해 행은 요청하기 전에 버퍼링됩니다 (네트워크 버퍼의 서버 쪽과 서버 쪽 모두). 따라서 첫 번째 .MoveNext 호출 이후에 버퍼에서 검색된 100 개의 행으로 끝낼 수 있습니다. 단 10 개만 읽었을지라도 말입니다.

오버 헤드는이 버퍼가 궁극적으로 고정 크기이므로 궁극적 인 문제는 아닙니다. 서버의 개수에 관계없이 결과 집합의 모든 행을 버퍼링하지 않고 버퍼링하지 않기 때문에 매우 비효율적입니다 일반적으로). 10 개의 행만 읽는 경우 쿼리가 완료 될 때 궁극적으로 1,000 또는 1,000,000 개의 행을 반환하는지 여부는 버퍼링 측면에서는 중요하지 않지만 주로 쿼리 계획 측면에서 중요합니다. 그럼에도 불구하고 이는 오버 헤드에 추가됩니다.

1

또한 페이지 번호 매기기 (0)와 테이크 (10)를보다 융통성있게 사용할 수 있습니다.

SQL 서버 2012

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

SQL 2005은 SQL 2012 # C

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 

2008 - 명령 : 최적화 여부부터

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