2012-07-19 218 views
0

我正在使用SQLite数据库,并将记录插入它。这需要很长时间!我看到有人说他们可以在一分钟内处理几千人。我有大约2400条记录。每条记录需要30s-2m才能完成。重新创建数据库不是一个选项。我试图以不同的方式创建一个事务。我需要使用计时器,因为我正在使用ProgressBar向我显示发生了某些事情。这里是我使用的代码:SQLite数据库插入非常缓慢

string con; 
con = string.Format(@"Data Source={0}", documentsFolder); 

SQLiteConnection sqlconnection = new SQLiteConnection(con); 
SQLiteCommand sqlComm = sqlconnection.CreateCommand(); 
sqlconnection.Open(); 
SQLiteTransaction transaction = sqlconnection.BeginTransaction(); 

Timer timer2 = new Timer(); 
timer2.Interval = 1000; 
timer2.Tick += (source, e) => 
        { 
         URL u = firefox.URLs[count2]; 
         string newtitle = u.title; 
         form.label1.Text = count2 + "/" + pBar.Maximum; 
         string c_urls = "insert or ignore into " + table + " (id, 
url, title, visit_count, typed_count, last_visit_time, hidden) values (" + dbID + ",'" + u.url + "','" 
    + newtitle + "',1,1, " + ToChromeTime(u.visited) + ", 0)"; 
         string c_visited = "insert or ignore into " + table2 + " (id, 
url, 
    visit_time, transition) values (" + dbID2 + "," + dbID + "," + 
ToChromeTime(u.visited) + ",805306368)"; 
         sqlComm = new SQLiteCommand(c_urls, sqlconnection); 
         sqlComm.ExecuteNonQuery(); 
         sqlComm = new SQLiteCommand(c_visited, sqlconnection); 
         sqlComm.ExecuteNonQuery(); 

         dbID++; 
         dbID2++; 


         pBar.Value = count2; 
         if (pBar.Maximum == count2) 
         { 
          pBar.Value = 0; 
          timer.Stop(); 
          transaction.Commit(); 
          sqlComm.Dispose(); 
          sqlconnection.Dispose(); 
          sqlconnection.Close(); 
         } 

         count2++; 
        }; 
timer2.Start(); 

我在做什么错?

+0

您是否试图直接对数据库运行insert命令(而不是通过C#)?您要插入的表格中有多少项目?它的模式是什么样的(字段+键+索引)?你在这张桌子上有触发器吗? – StriplingWarrior 2012-07-19 22:59:57

+0

好的,我插入到标准的Chrome历史记录数据库中。我插入了大约2400条记录。 – 2012-07-19 23:04:38

+0

@StriplingWarrior问你一些具体的问题(其中4个),你在回复中没有提到的问题。 “一个标准的Chrome历史记录数据库”不会回答“您是否尝试过直接对数据库运行?”,“表中有多少项?” (这与“插入多少项目”不同),或者向那些不熟悉“标准Chrome历史记录数据库”模式但熟悉运行SQLite插入语句的用户提供任何信息。 – 2012-07-19 23:09:35

回答

2

这就是我要解决的问题。它可能会或可能不会解决问题,但它不会伤害看到(和它可能只是做一些魔术):

  1. 确保数据库是与更新正在争辩(从另一个线程,过程,甚至定时器!)。作家将获得锁,而未关闭/超长运行的交易可能以不良方式进行交互。 (对于需要“30秒到2分钟”的更新,我会想象有一个获取锁的问题,并确保数据库所在的介质已足够,例如本地驱动器。)

  2. 该事务未被使用??)。将事务移动到的定时器回调中,并将其附加到相应的SQLCommands,并在回调结束前进行处理。 (使用using)。

  3. 并非所有SQLCommand的处理都正确。处理每一个。 (使用using简化了这一点,不要让它流过回调。)

  4. 占位符未被占用。这不仅更简单,更易于使用,而且对SQLite和适配器也更友好。

(例而已;有可能是在下面的代码中的错误。)

// It's okay to keep long-running SQLite connections. 
// In my applications I have a single application-wide connection. 
// The more important thing is watching thread-access and transactions. 
// In any case, we can keep this here. 
SQLiteConnection sqlconnection = new SQLiteConnection(con); 
sqlconnection.Open(); 

// In timer event - remember this is on the /UI/ thread. 
// DO NOT ALLOW CROSS-THREAD ACCESS TO THE SAME SQLite CONNECTION. 
// (You have been warned.) 
URL u = firefox.URLs[count2]; 
string newtitle = u.title; 
form.label1.Text = count2 + "/" + pBar.Maximum; 

try { 
    // This transaction is ONLY kept about for this timer callback. 
    // Great care must be taken with long-running transactions in SQLite. 
    // SQLite does not have good support for (long running) concurrent-writers 
    // because it must obtain exclusive file locks. 
    // There is no Table/Row locks! 
    sqlconnection.BeginTransaction(); 
    // using ensures cmd will be Disposed as appropriate. 
    using (var cmd = sqlconnection.CreateCommand()) { 
    // Using placeholders is cleaner. It shouldn't be an issue to 
    // re-create the SQLCommand because it can be cached in the adapter/driver 
    // (although I could be wrong on this, anyway, it's not "this issue" here). 
    cmd.CommandText = "insert or ignore into " + table 
     + " (id, url, title, visit_count, typed_count, last_visit_time, hidden)" 
     + " values (@dbID, @url, 'etc, add other parameters')"; 
    // Add each parameter; easy-peasy 
    cmd.Parameters.Add("@dbID", dbID); 
    cmd.Parameter.Add("@url", u.url); 
    // .. add other parameters 
    cmd.ExecuteNonQuery(); 
    } 
    // Do same for other command (runs in the same TX) 
    // Then commit TX 
    sqlconnection.Commit(); 
} catch (Exception ex) { 
    // Or fail TX and propagate exception .. 
    sqlconnection.Rollback(); 
    throw; 
} 

if (pBar.Maximum == count2) 
{ 
    pBar.Value = 0; 
    timer.Stop(); 
    // All the other SQLite resources are already 
    // cleaned up! 
    sqlconnection.Dispose(); 
    sqlconnection.Close(); 
} 
+0

你能举个例子吗? – 2012-07-20 22:04:47

+0

@CCInc更新为“我只是输入”的例子。 – 2012-07-20 23:38:30

+0

@CCInc此外,验证* other *数据库用来确保长时间运行的事务不会导致[小]死锁。 – 2012-07-20 23:40:46

2

我不知道如果这是你的问题,但使用ADO.NET你的一般模式是错误的 - 您不应为每次插入创建新命令(并重复支付查询准备工作)。

相反,做到以下几点:

  • 循环之前:
    • 创建命令(S)一次。
    • 创建适当的绑定参数。
  • 在循环:
    • 刚分配相应的值到绑定的参数。
    • 并执行命令。

你也可以考虑使用较少的细粒度交易:尝试将几个刀片在同一事务,以尽量减少paying for transaction durability。您可能还想看看this post