J'ai récemment lu sur SQLite et j'ai pensé que je pourrais l'essayer. Lorsque j'insère un enregistrement, il fonctionne correctement. Mais lorsque j'en insère cent, cela prend cinq secondes, et à mesure que le nombre d'enregistrements augmente, le temps augmente. Qu'est-ce qui ne va pas? J'utilise le 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();
Enveloppez les instructions BEGIN
\END
autour de vos insertions groupées. Sqlite est optimisé pour les transactions.
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();
Essayez de regrouper toutes vos insertions (aka, une insertion en vrac) dans une seule 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();
}
Par défaut, SQLite encapsule toutes les insertions dans une transaction , ce qui ralentit le processus:
INSERT est vraiment lent - je ne peux faire que quelques dizaines d'inserts par seconde
En fait, SQLite exécutera facilement 50 000 instructions INSERT ou plus par seconde sur un ordinateur de bureau moyen. Mais il ne fera que quelques dizaines de transactions par seconde.
La vitesse de transaction est limitée par la vitesse du lecteur de disque car (par défaut) SQLite attend réellement que les données soient réellement stockées en toute sécurité sur la surface du disque avant la fin de la transaction. De cette façon, si vous perdez soudainement de l'énergie ou si votre système d'exploitation tombe en panne, vos données sont toujours en sécurité. Pour plus de détails, lisez sur la validation atomique dans SQLite.
Par défaut, chaque instruction INSERT est sa propre transaction. Mais si vous entourez plusieurs instructions INSERT avec BEGIN ... COMMIT, toutes les insertions sont regroupées en une seule transaction. Le temps nécessaire pour valider la transaction est amorti sur toutes les instructions d'insertion jointes et donc le temps par instruction d'insertion est considérablement réduit.
J'ai lu partout que la création de transactions est la solution pour ralentir les écritures SQLite, mais il peut être long et pénible de réécrire votre code et d'envelopper toutes vos écritures SQLite dans des transactions.
J'ai trouvé une méthode beaucoup plus simple, sûre et très efficace: j'active une optimisation SQLite 3.7.0 (désactivée par défaut): le Write-Ahead-Log (WAL) . La documentation indique que cela fonctionne dans tous les systèmes Unix (c'est-à-dire Linux et OSX) et Windows.
Comment ? Exécutez simplement les commandes suivantes après avoir initialisé votre connexion SQLite:
PRAGMA journal_mode = WAL
PRAGMA synchronous = NORMAL
Mon code s'exécute désormais ~ 600% plus rapidement: ma suite de tests s'exécute désormais en 38 secondes au lieu de 4 minutes :)
Voir "Optimisation des requêtes SQL" dans le fichier d'aide ADO.NET SQLite.NET.chm. Code de cette page:
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();
}