2010-05-19 4 views
10

Я использую SP, и это не вопрос с кодом «Застроить ваш SQL-код», а не код SP. Я ищу метод высокой пропускной способности для бэкэнд-приложения, которое обрабатывает многие мелкие транзакции. Я использую SQLDataReader для большей части возвратов, так как форвард работает только в большинстве случаев для меня.Самый быстрый метод для SQL Server вставляет, обновляет, выбирает

Я видел это много, и использовал большинство из них сам.

  1. Методы, которые определяют и принимают сохраненные параметры процедуры, как сами параметры и построить с помощью cmd.Parameters.Add (с или без указания типа значения DB и/или длину)

  2. Сборка вашего SP Params и их значения в массив или хэш-таблицу, затем переходят к более абстрактному методу, который анализирует коллекцию, а затем запускает cmd.Parameters.Add

  3. Классы, которые представляют таблицы, инициализируя класс по необходимости, устанавливая общедоступные свойства, которые представляют поля таблицы и методы вызова lik e Сохранение, загрузка и т. д.

Я уверен, что есть и другие, которых я видел, но не могу вспомнить и на данный момент. Я открыт для всех предложений.

ответ

29

В этом ответе основное внимание уделяется операциям «select» vs update/create/delete. Я думаю, что реже обновлять более одного или нескольких записей за раз, и поэтому я также думаю, что «выбор» - это то место, где узкие места имеют тенденцию возникать. Тем не менее, вам нужно знать ваше приложение (профиль). Лучшее место для фокусировки вашего времени оптимизации почти всегда находится на уровне базы данных в самих запросах, а не на клиентском коде. Клиентский код - это всего лишь сантехника: это не основная сила вашего приложения. Однако, поскольку сантехника, как правило, повторно используется во многих разных приложениях, я сочувствую желанию максимально приблизить ее к оптимальному, и поэтому у меня есть много возможностей сказать, как построить этот код.

У меня есть универсальный метод для некоторых запросов/процедур в моем слое данных, который выглядит примерно так:

private static IEnumerable<IDataRecord> Retrieve(string sql, Action<SqlParameterCollection> addParameters) 
{ 
    //ConnectionString is a private static property in the data layer 
    // You can implement it to read from a config file or elsewhere 
    using (var cn = new SqlConnection(ConnectionString)) 
    using (var cmd = new SqlCommand(sql, cn)) 
    { 
     addParameters(cmd.Parameters); 

     cn.Open(); 
     using (var rdr = cmd.ExecuteReader()) 
     { 
      while (rdr.Read()) 
       yield return rdr; 
      rdr.Close(); 
     } 
    } 
} 

И это позволяет мне писать методы общественных слоев данных, которые используют анонимные методы для добавления параметров. Код, показанный работы с .Net 2.0+, но можно записать еще короче, используя .NET 3.5:

public IEnumerable<IDataRecord> GetFooChildrenByParentID(int ParentID) 
{ 
    //I could easily use a stored procedure name instead of a full sql query 
    return Retrieve(
     @"SELECT c.* 
     FROM [ParentTable] p 
     INNER JOIN [ChildTable] c ON c.ParentID = f.ID 
     WHERE f.ID= @ParentID", delegate(SqlParameterCollection p) 
     { 
      p.Add("@ParentID", SqlDbType.Int).Value = ParentID; 
     } 
    ); 
} 

Я собираюсь остановиться прямо здесь, так что я могу указать вам еще раз код чуть выше который использует анонимный метод для создания параметров.

Это очень чистый код, в котором он ставит определение запроса и создание параметров в одном и том же месте, в то же время позволяя абстрагироваться от кода подключения/вызова кода шаблона к чему-то более пригодному для повторного использования. Я не думаю, что эта техника покрыта какой-либо из пулевых точек в вашем вопросе, и это тоже довольно быстро. Я думаю, что это касается вопроса вашего вопроса.


Я хочу продолжить, хотя, чтобы объяснить, как это все совмещается. Остальное довольно прямолинейно, но также легко бросить это в список или подобное, и что-то не так, что в конечном итоге ухудшает производительность. Таким образом, бизнес-уровень затем использует фабрику для преобразования результатов запроса в объекты (C# 3.0 или более поздняя версия):

public class Foo 
{ 
    //various normal properties and methods go here 

    public static Foo FooFactory(IDataRecord record) 
    { 
     return new Foo 
     { 
      Property1 = record[0], 
      Property2 = record[1] 
      //... 
     }; 
    } 
} 

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

Мне нужно внести одно изменение в исходный метод извлечения. Этот метод «возвращает» один и тот же объект снова и снова, и это не всегда так хорошо работает. То, что мы хотим сделать по-другому, чтобы заставить его работать, - заставить копию объекта, представленного текущей записью, так что, когда читатель мутирует для следующей записи, мы работаем с чистыми данными. Я ждал, пока не покажу заводский метод, чтобы мы могли использовать это в конечном коде. Новый Восстановите метод выглядит следующим образом:

private static IEnumerable<T> Retrieve(Func<IDataRecord, T> factory, 
        string sql, Action<SqlParameterCollection> addParameters) 
{ 
    //ConnectionString is a private static property in the data layer 
    // You can implement it to read from a config file or elsewhere 
    using (var cn = new SqlConnection(ConnectionString)) 
    using (var cmd = new SqlCommand(sql, cn)) 
    { 
     addParameters(cmd.Parameters); 

     cn.Open(); 
     using (var rdr = cmd.ExecuteReader()) 
     { 
      while (rdr.Read()) 
       yield return factory(rdr); 
      rdr.Close(); 
     } 
    } 
} 

И теперь мы называем этот новый метод Получить(), как это:

public IEnumerable<Foo> GetFooChildrenByParentID(int ParentID) 
{ 
    //I could easily use a stored procedure name instead of a full sql query 
    return Retrieve(Foo.FooFactory, 
     @"SELECT c.* 
     FROM [ParentTable] p 
     INNER JOIN [ChildTable] c ON c.ParentID = f.ID 
     WHERE f.ID= @ParentID", delegate(SqlParameterCollection p) 
     { 
      p.Add("@ParentID", SqlDbType.Int).Value = ParentID; 
     } 
    ); 
} 

Очевидно, что этот последний метод может быть расширен, чтобы включать в себя любую дополнительную бизнес-логику, необходимую , Также выясняется, что этот код является исключительно быстрым, потому что он использует ленивые функции оценки IEnumerable. Недостатком является то, что он имеет тенденцию создавать много короткоживущих объектов, что может повредить транзакционную производительность, о которой вы просили. Чтобы обойти это, я иногда ломаю хороший n-уровень и передаю объекты IDataRecord непосредственно на уровень представления и избегаю ненужного создания объектов для записей, которые просто привязаны к элементу управления сетью сразу.

Обновление/создание кода аналогично, с той разницей, что вы обычно меняете только одну запись за раз, а не многие.

Или я мог бы спасти вас читают эту длинную почту и просто посоветует использовать Entity Framework;)

+1

Хороший трюк, действие <> обратного вызова параметризовать запрос и закрытие анонимный делегат. –

+0

+1, для последней строки :) –

+0

Поместите последнюю строку вверху. :-) –

3

Fastest для времени выполнения или быстро на время программирования? Единственное, что вы могли бы сделать, чтобы увеличить пропускную способность на # 1, - это использовать несколько потоков и соединений для вставки - вы можете сделать это с помощью SQLCommand.BeginExecuteNonQuery

8

Лично я большой поклонник генерации кода. Я откатываю свой собственный XML доморощенного, и во время сборки я запускаю его через XSLT для создания моих файлов .CS. Я описываю процесс в этом сообщении Using XSLT to generate Performance Counters code. Хотя в ссылке обсуждается генерация кода счетчиков производительности, я использую тот же процесс для генерации моего DAL.

Так что я бы создать XML, как:

<query name="LoadCustomerByName" returns="Customer"> 
    <parameter name="name" type="String"/> 
    <text>SELECT ... FROM Customers WHERE [email protected]</text> 
</query> 

, а затем XLST бы превратить это в нечто вроде:

public Customer LoadCustomerByName(
    SqlConnection conn, 
    SqlTransaction trn, 
    String name) 
{ 
    using (Sqlcommand cmd = new SqlCommand(@"SELECT ... FROM ...", conn, trn)) 
    { 
    cmd.Parameters.AddWithValue("@name", name); 
    using (SqlDataReader rdr = cmd.ExecuteReader()) 
    { 
     Customer c = new Customer(); 
     // Load c from rdr 
     return c; 
    } 
    } 
} 

Теперь я выпустил много деталей о том, что преобразования XSLT на самом деле, но действительно важно то, что этот метод дает мне абсолютный контроль над тем, как я создаю свой DAL, и он гибкий во всех аспектах, поскольку сгенерированный код .CS полностью управляется моими XSLT. Я могу изменить XLST, и это приведет к повторному генерации каждого отдельного метода в моем DAL. Это позволяет легко исследовать различные решения, это позволяет мне добавлять инструменты в код (например, счетчики для измерения производительности каждого отдельного запроса и частоты использования) и многие другие.

Это то, что в принципе различные дизайнеры VS делают для вас, но если вы сделаете дополнительный шаг для управления процессом создания кода, у вас будет намного больше гибкости и контроля над результатом.

0

Единственное, что мне не нравится, это то, что доходность возврата cannor находится внутри try ... catch block. Поэтому централизованное управление/обработка исключений невозможно.

Я использовал аналогичную оценку, но передаю IEnumerable в качестве параметра. Тогда мне не нужно использовать возврат доходности.

1

Речь идет не о вставке или обновлении, но я сделал некоторый бенчмаркинг для скорости чтения с различными подходами. Of all, DataTable routes seemed slower of the bunch.. Подход Джоэла в основном самый быстрый, который вы можете получить.

2

Это было какое-то время, но если вы открыты для использования очень Micro-ORM, просто используйте dapper-dot-net.

У этого есть очень чистый синтаксис, чрезвычайно быстрый и легко бросающийся в любой проект. Он используется в производстве в StackOverflow, и эта страница, скорее всего, была принесена вам. Поддерживает все обычные методы SQL в качестве методов расширения и поддерживает async на всех.

Некоторые сравнения производительности:

Смежные вопросы