2016-07-27 3 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 клиентов. Я мог бы сделать

LoadCustomers.Take(10) 

или передать 10 в качестве параметра SQL и сделать мой SQL

SELECT TOP 10 FROM Customers ORDER BY CreationDate DESC 

По this post весь набор результатов передается от SQL сервера к клиенту, даже если DataReader читает только несколько строк (до тех пор, пока соединение открыто) - следует ли мне избегать подхода Take(10) из-за того, что дополнительные данные передаются в любом случае клиенту, или это будет преждевременная оптимизация, чтобы избежать этого (потому что код возврата доходности закроет соединение после он читает 10 строк, а затем передача данных все равно остановится)?

+2

'ORDER BY CreationDate DESC'? – jarlh

+4

Это не была бы преждевременная оптимизация. Возьмите из базы данных только то, что вам действительно нужно. Нет смысла выбирать 10 000 клиентов, когда вам нужно всего 10. –

+1

Вы неправильно истолковываете то, что говорит этот пост. Весь результирующий набор * не * передается клиенту, если вы прекратите чтение, хотя некоторые строки уже могут быть буферизованы. 'SqlDataReader' не« читает вперед »за пределами сети. Причина, по которой вы по-прежнему хотите отправить «TOP (10)» на сервер базы данных в большинстве случаев и почему это не является преждевременной оптимизацией, заключается в том, что оптимизатор может создать более эффективный план, если он знает, что вам нужно только 10 строк, а не чтение всей таблицы (если ничего другого, запрос будет выделять меньше памяти заранее). –

ответ

2

Так является ли оптимизация «преждевременно» субъективно, я решил интерпретировать этот вопрос как «делает с помощью DataReader и остановка чтения после 10 строк имеет те же рабочие характеристики, что и использование TOP(10) в запросе? "

Ответ отрицательный. Передача TOP(10) на сервер позволяет оптимизатору настраивать чтения, гранты памяти, буферы ввода-вывода, блокировку и параллельность блокировки, зная, что запрос вернет (и в этом случае также прочитает) не более 10 строк. Оставляя TOP, он должен подготовиться к случаю, когда клиент будет читать все строки - независимо от того, остановитесь ли вы раньше.

Неверно, что сервер будет отправлять строки независимо от того, читаете их или нет. Вытягивание строк с помощью SqlDataReader концептуально выполняется по очереди: когда вы выдаете Reader.MoveNext, вы выбираете следующую строку с сервера и только эту строку. Но в интересах производительности строки буферизуются перед запросом (как на сервере, так и в сетевых буферах). Таким образом, можно получить, скажем, 100 строк, полученных в буферах после первого вызова .MoveNext, даже если вы прочитали только 10 из них.

Что касается накладных расходов, это не будет моей главной заботой, потому что эти буферы в конечном итоге имеют фиксированный размер: сервер не пойдет и не забудет все строки набора результатов независимо от того, сколько их (это было бы очень неэффективно в целом). Если вы читаете только 10 строк, будет ли ваш запрос в конечном итоге возвращать 1000 или 1 000 000 строк, если он завершится до завершения, это не будет иметь значения с точки зрения буферизации, но прежде всего с точки зрения плана запроса. Тем не менее, это добавляет дополнительные накладные расходы.

1

Вы также можете использовать разбивку на страницы (0) и принять (10) большую гибкость.

SQL SERVER 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# с 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)); 
        } 
       } 
      } 
Смежные вопросы