C # -SQL: Как выполнить пакет StoredProcedure? - PullRequest
1 голос
/ 14 октября 2008

Редактировать:
Моя проблема больше не является проблемой: я повторил свои тесты производительности и сделал фатальную глупую ошибку: я забыл x1000, чтобы получить секунды из миллисекунд: / Извините за это, ребята.
Для информации:
- Я выполняю около 1900 обновлений в секунду с моего компьютера на сервер базы данных в локальной сети.
- 3.200 обновлений в секунду, если программы находятся на той же машине, что и БД.
- 3.500 обновлений в секунду с моего ПК на сервере базы данных. Я не воссоздаю и заново не открываю новый SQLConnection.
- 5.800 обновлений в секунду с пакетным текстом. Для моих 10.000 строк, если это займет 5 секунд, это нормально для моих программ. Извините, что беспокою вас.

На самом деле я использую хранимую процедуру SQL для создания строки в моей базе данных, чтобы избежать SQL-инъекций. В C # у меня есть следующий метод:

public void InsertUser(string userCode)
{
   using (SqlConnection sqlConnection = new SqlConnection(this.connectionString))
   {
      SqlCommand sqlCommand = new SqlCommand("InsertUser", sqlConnection);
      sqlCommand.CommandType = System.Data.CommandType.StoredProcedure;
      sqlCommand.Parameters.Add(new SqlParameter("@UserCode", userCode));
      sqlConnection.Open();
      sqlCommand.ExecuteNonQuery();///0.2 seconds !ERROR HERE! 0.2ms here,NOT 0.2sec!!!
   }
} 

Прекрасно просыпается, когда мне нужно вставить один или два ряда. Но если мне нужно создать 1.000 пользователей, 10.000 продуктов и 5000 питомцев, это не лучшее решение: я потеряю огромное количество времени на транспорте в сети.

Я верю, без регистрации, что я могу использовать только ограниченное количество обратных вызовов. Поэтому я не хочу звонить 10.000 раз:

sqlCommand.BeginExecuteNonQuery()

Другим способом будет создание пакетного текста, но существует риск внедрения SQL-кода (и это некрасиво).

Существует ли объект 'SqlCommandList', который управляет этим в .Net? Как сделать большую запись в базе данных? Какой хороший образец для этого?

Ответы [ 9 ]

10 голосов
/ 14 октября 2008

Это должно работать немного быстрее:

public void InsertUser(IEnumerable<string> userCodes)
{
   using (SqlConnection sqlConnection = new SqlConnection(this.connectionString), 
             SqlCommand sqlCommand = new SqlCommand("InsertUser", sqlConnection))
   {
      sqlCommand.CommandType = System.Data.CommandType.StoredProcedure;
      SqlParameter param = sqlCommand.Parameters.Add("@UserCode", SqlDbTypes.VarChar);
      sqlConnection.Open();

      foreach(string code in userCodes)
      {
          param.Value = code;
          sqlCommand.ExecuteNonQuery();///0.2 seconds
      }
   }
}

Это откроет только одно соединение и создаст только одну команду, даже если вы передадите ей 1000 пользователей. Это все еще сделает каждую вставку отдельно, все же. И, конечно же, если userCode не является строкой, вы захотите соответствующим образом изменить ее фактор. Вы также можете заглянуть в SQL Server BULK INSERT .

3 голосов
/ 14 октября 2008

А как насчет UpdateBatchSize для SQLDataAdaptor?

Наши ребята из фронтэнда используют это для пакетирования нескольких 10 000 вызовов в процессах

Статья

1010 * MSDN *

В нашей среде запрещены права "bulkadmin", поэтому мы не можем использовать BULKINSERT / bcp и т. Д.

2 голосов
/ 14 октября 2008

Исходя из ответа Джоэла, это самое быстрое решение, за исключением использования SqlBulkCopy или создания больших цепочек грязного SQL и выполнения. (Я добавил транзакцию, которая значительно улучшит производительность)

public void InsertUser(IEnumerabler<string> userCodes)
{
   using (SqlConnection sqlConnection = new SqlConnection(this.connectionString))
   {
      sqlConnection.Open();
      SqlTransaction transaction = connection.BeginTransaction();
      SqlCommand sqlCommand = new SqlCommand("InsertUser", sqlConnection);
      sqlCommand.Transaction = transaction;
      sqlCommand.CommandType = System.Data.CommandType.StoredProcedure;
      SqlParameter param = sqlCommand.Parameters.Add("@UserCode", SqlDbTypes.VarChar);

      foreach(string code in userCodes)
      {
          param.Value = code;
          sqlCommand.ExecuteNonQuery();
      }      
      transaction.Commit();
   }
}
2 голосов
/ 14 октября 2008

Если вы действительно обеспокоены этим, вы можете (как вы сказали) объединить команды в строки следующим образом:

var cmd = new SqlCommand();
cmd.Connection = sqlConnection;
for (int i = 0; i < batchSize; i++) {
    cmd.CommandText += String.Format("EXEC InsertUser @UserCode{0};", i);
    cmd.Parameters.AddWithValue("@UserCode" + i.ToString(), XXXXX);
    //... etc ...
}

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

2 голосов
/ 14 октября 2008

Лично, если я регулярно планирую делать довольно большие вставки (10 000 строк определенно будут соответствовать ...), я мог бы рассмотреть возможность иметь отдельную таблицу для входящих данных и использовать SqlBulkCopy для заполнения этого Таблица. Затем вы просто выполняете одну хранимую процедуру, которая перемещает данные в реальную таблицу.

Другой подход - отправить xml в базу данных и использовать sqlxml для его анализа (намного проще с SQL2005 и выше), но это требует дополнительной работы на сервере БД.

1 голос
/ 12 декабря 2011

Полагаю, это довольно старый вопрос.

В SQL Server 2008 ответ теперь заключается в использовании параметра табличного значения. Короче говоря, передайте все свои переменные в используемом определенном типе (таблица).

В SQL теперь вы можете обрабатывать все записи как отдельные элементы ... На самом деле используйте заданную логику и получите реальную производительность.

0 голосов
/ 14 октября 2008

Согласно некоторым ответам выше, наиболее заметное увеличение производительности при минимальных усилиях включает 2 изменения в существующем коде:

  1. Упаковка обновлений в транзакцию
  2. Открытие только одного соединения и многократный вызов процедуры с разными параметрами.

BULK INSERTs - вариант, но, вероятно, излишний для того, что вы хотите сделать.

0 голосов
/ 14 октября 2008

«это не лучшее решение: я потеряю огромное количество времени на транспорте в сети» Можете ли вы жить с потерей?

Если это то, что вы делаете не часто, то имеет ли это значение? Сначала измерить, если это проблема, то исправить, лично, вероятно, я бы пошел с таблицей Marc Gravells для входящих вставок. Другой вариант - запускать вставки асинхронно, тогда вы не дожидаетесь окончания каждого из них, прежде чем начнете следующее.

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

Надеюсь, это поможет (хотя я не думаю, что это извинит).

0 голосов
/ 14 октября 2008

Рассматривали ли вы передачу документа XML в хранимую процедуру, а затем итерацию по ней для поиска данных для вставки?

...