SQLite Вставить очень медленно?

Недавно я прочитал о SQLite и думал, что попробую. Когда я вставляю одну запись, она работает нормально. Но когда я вставляю сто, это занимает пять секунд, и по мере увеличения количества записей так же происходит время. Что может быть неправильным? Я использую SQLite Wrapper (system.data.SQlite):

dbcon = new SQLiteConnection(connectionString);
dbcon.Open();

//---INSIDE LOOP

 SQLiteCommand sqlComm = new SQLiteCommand(sqlQuery, dbcon);

 nRowUpdatedCount = sqlComm.ExecuteNonQuery(); 

//---END LOOP

dbcon.close();

Ответ 1

Оберните BEGIN\END заявления вокруг ваших объемных вставок. Sqlite оптимизирован для транзакций.

dbcon = new SQLiteConnection(connectionString);
dbcon.Open();

SQLiteCommand sqlComm;
sqlComm = new SQLiteCommand("begin", dbcon);
sqlComm.ExecuteNonQuery(); 
//---INSIDE LOOP

 sqlComm = new SQLiteCommand(sqlQuery, dbcon);

 nRowUpdatedCount = sqlComm.ExecuteNonQuery(); 

//---END LOOP
sqlComm = new SQLiteCommand("end", dbcon);
sqlComm.ExecuteNonQuery(); 
dbcon.close();

Ответ 2

Попробуйте обернуть все ваши вставки (ака, объемную вставку) в один transaction:

string insertString = "INSERT INTO [TableName] ([ColumnName]) Values (@value)";

SQLiteCommand command = new SQLiteCommand();
command.Parameters.AddWithValue("@value", value);
command.CommandText = insertString;
command.Connection = dbConnection;
SQLiteTransaction transaction = dbConnection.BeginTransaction();
try
{
    //---INSIDE LOOP
    SQLiteCommand sqlComm = new SQLiteCommand(sqlQuery, dbcon);
    nRowUpdatedCount = sqlComm.ExecuteNonQuery(); 
    //---END LOOP

    transaction.Commit();
    return true;
}
catch (SQLiteException ex)
{
    transaction.Rollback();
}

По умолчанию SQLite обертывает все вставки в транзакции, что замедляет процесс:

INSERT действительно медленный - я могу делать только несколько десятков INSERT в секунду

Фактически SQLite будет легко выполнять 50 000 или более инструкций INSERT в секунду на среднем настольном компьютере. Но это займет всего несколько десятков транзакций в секунду.

Скорость транзакций ограничена скоростью диска, потому что (по умолчанию) SQLite фактически ожидает, пока данные действительно будут сохранены на поверхности диска до завершения транзакции. Таким образом, если вы внезапно потеряете власть или если ваша ОС выйдет из строя, ваши данные по-прежнему будут в безопасности. Для получения дополнительной информации читайте об атомарной фиксации в SQLite..

По умолчанию каждый оператор INSERT является собственной транзакцией. Но если вы окружите несколько инструкций INSERT с помощью BEGIN... COMMIT, тогда все вставки сгруппированы в одну транзакцию. Время, необходимое для фиксации транзакции, амортизируется по всем прилагаемым операторам вставки, и поэтому время для инструкции вставки значительно уменьшается.

Ответ 3

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

Я нашел намного более простой, безопасный и очень эффективный метод: я включаю (отключено по умолчанию) оптимизацию SQLite 3.7.0: Write-Ahead-Log (WAL). В документации говорится, что он работает во всех Unix (например, Linux и OSX) и системах Windows.

Как? Просто выполните следующие команды после инициализации соединения SQLite:

PRAGMA journal_mode = WAL
PRAGMA synchronous = NORMAL

Мой код теперь работает примерно на 600% быстрее: мой набор тестов теперь работает через 38 секунд вместо 4 минут:)

Ответ 4

См. "Оптимизация SQL-запросов" в файле справки ADO.NET SQLite.NET.chm. Код с этой страницы:

using (SQLiteTransaction mytransaction = myconnection.BeginTransaction())
{
  using (SQLiteCommand mycommand = new SQLiteCommand(myconnection))
  {
    SQLiteParameter myparam = new SQLiteParameter();
    int n;

    mycommand.CommandText = "INSERT INTO [MyTable] ([MyId]) VALUES(?)";
    mycommand.Parameters.Add(myparam);

    for (n = 0; n < 100000; n ++)
    {
      myparam.Value = n + 1;
      mycommand.ExecuteNonQuery();
    }
  }
  mytransaction.Commit();
}