【问题标题】:Inserting 34000 records from API to sqlite in iphone将 34000 条记录从 API 插入到 iphone 中的 sqlite
【发布时间】:2012-03-15 11:34:32
【问题描述】:

我必须从从 JSON API 获取的数据中创建一个 sqlite 数据库。代码运行良好,它通过 for 循环逐个添加它们,但 api 响应时间为每次命中 1 秒,因此 34000 秒加上通过代码将它们插入 sqlite 将需要大约 9 小时。有什么办法可以加快速度吗?

编辑:我正在使用 Objective C/sqlite3 框架/Xcode 4.2

这是代码...

 dbPath=[self.databasePath UTF8String];
if(sqlite3_open(dbPath,&database)==SQLITE_OK)
{
  //   sqlite3_exec(database, "BEGIN", 0, 0, 0);
    const char *sqlstatement="insert into artist values(?,?,?,?,?)";
    sqlite3_stmt *compiledstatement;

    if(sqlite3_prepare_v2(database,sqlstatement , -1, &compiledstatement, NULL)==SQLITE_OK)
    {    
for(i=4611;i<=34803;i++)
{  
    NSURLResponse *response;
    NSError *err;
  NSData *data= [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"API&id=%i",i]]] returningResponse:&response error:&err];
    if(data.length>0)
    {
        NSError *err;
        NSDictionary *jsonDict=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&err];
        // sqlite3_exec(database, "BEGIN", 0, 0, 0);




                sqlite3_bind_text(compiledstatement,1,[[[[jsonDict objectForKey:@"artistDetail"]objectForKey:@"artist"]objectForKey:@"id"] UTF8String], -1, SQLITE_TRANSIENT);
                sqlite3_bind_text(compiledstatement,2,[[[[jsonDict objectForKey:@"artistDetail"]objectForKey:@"artist"]objectForKey:@"name"] UTF8String], -1, SQLITE_TRANSIENT);
                if([[[jsonDict objectForKey:@"artistDetail"]objectForKey:@"artist"]objectForKey:@"description"])
                    sqlite3_bind_text(compiledstatement,3,[[[[jsonDict objectForKey:@"artistDetail"]objectForKey:@"artist"]objectForKey:@"description"] UTF8String], -1, SQLITE_TRANSIENT);
                else
                    sqlite3_bind_text(compiledstatement,3,[@"" UTF8String], -1, SQLITE_TRANSIENT);
                if([[[[jsonDict objectForKey:@"artistDetail"]objectForKey:@"artist"]objectForKey:@"links"]objectForKey:@"website"])
                    sqlite3_bind_text(compiledstatement,4,[[[[[jsonDict objectForKey:@"artistDetail"]objectForKey:@"artist"]objectForKey:@"links"]objectForKey:@"website"] UTF8String], -1, SQLITE_TRANSIENT);
                else
                    sqlite3_bind_text(compiledstatement,4,[@"" UTF8String], -1, SQLITE_TRANSIENT);
                if([[[[[[jsonDict objectForKey:@"artistDetail"]objectForKey:@"artist"]objectForKey:@"media"]objectForKey:@"low_res_images"]objectAtIndex:0]objectForKey:@"url"])
                    sqlite3_bind_text(compiledstatement,5,[[[[[[[jsonDict objectForKey:@"artistDetail"]objectForKey:@"artist"]objectForKey:@"media"]objectForKey:@"low_res_images"]objectAtIndex:0] objectForKey:@"url"] UTF8String], -1, SQLITE_TRANSIENT);
                else
                    sqlite3_bind_text(compiledstatement,5,[@"" UTF8String], -1, SQLITE_TRANSIENT);
                if(sqlite3_step(compiledstatement)==SQLITE_DONE)
                {
                    NSLog(@"done %i",i);
                }
                else NSLog(@"ERROR");


        }
        sqlite3_reset(compiledstatement);
                }

    }

}

else
    NSLog(@"error");

sqlite3_close(database);

【问题讨论】:

  • 34K 记录需要 9 小时?你做错了什么……
  • @MitchWheat 我已经发布了代码。请帮忙
  • 这些记录经常变化吗?您可以在您的计算机上创建数据库并将其包含在项目中吗?
  • @JoshRagem 除了 objc 之外,还有什么更快的方法可以从 json API 填充这个 sqlite?
  • @Akash 我不知道,但是如果您获得的记录不经常更改,我认为您可以在桌面上填充 SQLite 数据库并将文件放入项目中,这样就可以了应用程序的一部分。然后,随着记录的变化,您可以通过应用程序逐步更新数据库。一个简单的“最后更新”日期戳足以让您用于检查更新。这看起来合理吗?

标签: iphone json performance sqlite insert


【解决方案1】:

是否可以重新构建您的代码,这样您就不会在每次迭代时都打开数据库?

  • 打开数据库
  • 开始事务 sqlite3_exec(..., "BEGIN", ...)
  • 编译语句
  • 迭代数据集
    • 插入记录
  • 完成编译语句
  • 提交事务 sqlite3_exec(..., {"ROLLBACK" or "COMMIT"}, ...)
  • 关闭数据库

这与你现在所拥有的相反

  • 迭代数据集
    • 打开数据库
    • 编译语句
    • 插入记录
    • 完成编译语句
    • 关闭数据库

按照您的操作方式进行操作的开销会影响性能。尝试重构为我上面概述的方法,看看效果如何。

编辑

我已重新格式化您的代码以指明我在说什么。此外,我认为您所接受的另一个性能(如另一位用户所示)是 JSON 调用。这可能是真正让你慢下来的原因。

dbPath=[self.databasePath UTF8String];
if(sqlite3_open(dbPath,&database)==SQLITE_OK)
{
    sqlite3_exec(database, "BEGIN", 0, 0, 0);
    const char *sqlstatement="insert into artist values(?,?,?,?,?)";
    sqlite3_stmt *compiledstatement;

    if(sqlite3_prepare_v2(database,sqlstatement , -1, &compiledstatement, NULL)==SQLITE_OK)
    {
        int hasError= 0;
        for(i=4611; hasError == 0 && i<=34803; i++)
        {  
            NSURLResponse *response;
            NSError *err;
            NSData *data= [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"API&id=%i",i]]] returningResponse:&response error:&err];
            if(data.length>0)
            {
                NSDictionary *jsonDict=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&err];
                // sqlite3_exec(database, "BEGIN", 0, 0, 0);
                sqlite3_bind_text(compiledstatement,1,[[[[jsonDict objectForKey:@"artistDetail"]objectForKey:@"artist"]objectForKey:@"id"] UTF8String], -1, SQLITE_TRANSIENT);
                sqlite3_bind_text(compiledstatement,2,[[[[jsonDict objectForKey:@"artistDetail"]objectForKey:@"artist"]objectForKey:@"name"] UTF8String], -1, SQLITE_TRANSIENT);
                if([[[jsonDict objectForKey:@"artistDetail"]objectForKey:@"artist"]objectForKey:@"description"])
                    sqlite3_bind_text(compiledstatement,3,[[[[jsonDict objectForKey:@"artistDetail"]objectForKey:@"artist"]objectForKey:@"description"] UTF8String], -1, SQLITE_TRANSIENT);
                else
                    sqlite3_bind_text(compiledstatement,3,[@"" UTF8String], -1, SQLITE_TRANSIENT);
                if([[[[jsonDict objectForKey:@"artistDetail"]objectForKey:@"artist"]objectForKey:@"links"]objectForKey:@"website"])
                    sqlite3_bind_text(compiledstatement,4,[[[[[jsonDict objectForKey:@"artistDetail"]objectForKey:@"artist"]objectForKey:@"links"]objectForKey:@"website"] UTF8String], -1, SQLITE_TRANSIENT);
                else
                    sqlite3_bind_text(compiledstatement,4,[@"" UTF8String], -1, SQLITE_TRANSIENT);
                if([[[[[[jsonDict objectForKey:@"artistDetail"]objectForKey:@"artist"]objectForKey:@"media"]objectForKey:@"low_res_images"]objectAtIndex:0]objectForKey:@"url"])
                    sqlite3_bind_text(compiledstatement,5,[[[[[[[jsonDict objectForKey:@"artistDetail"]objectForKey:@"artist"]objectForKey:@"media"]objectForKey:@"low_res_images"]objectAtIndex:0] objectForKey:@"url"] UTF8String], -1, SQLITE_TRANSIENT);
                else
                    sqlite3_bind_text(compiledstatement,5,[@"" UTF8String], -1, SQLITE_TRANSIENT);
                if(sqlite3_step(compiledstatement)==SQLITE_DONE)
                {
                    NSLog(@"done %i",i);
                }
                else {
                    NSLog(@"ERROR");
                    hasError= 1;
                }
            }
            sqlite3_reset(compiledstatement);
        }
        // Really need to check error conditions with commit/rollback
        if( hasError == 0 ) {
            sqlite3_exec(database, "COMMIT", 0, 0, 0);
        }
        else {
            sqlite3_exec(database, "ROLLBACK", 0, 0, 0);
        }
    }
    sqlite3_close(database);
}
else {
    NSLog(@"error");
}

【讨论】:

  • 另一件事是数据库默认处于自动提交模式。您可能希望在进入循环之前(可能在编译语句之前)执行 BEGIN 语句,然后在成功完成时执行 COMMIT 或在中间某处发生故障时执行 ROLLBACK。这样,您的数据库就不会因为运行不良而混乱。
  • 我已经通过删除插入编译 stmt 并在 for 循环外打开 stmt 来尝试您的方式,仍然是它的速度。我在上面更新了我的代码。有什么想法吗?
  • 您在每次迭代中重新准备语句,使用 sqlite3_prepare_v2 将其移出循环
  • 还可以尝试循环外的 BEGIN/{ROL​​LBACK,COMMIT} ,每次按照配置运行 sqlite3_step 时,您都会在每次迭代中打开/提交事务。通过在循环之前执行 BEGIN,您打开一个用于整个迭代的事务,然后在循环结束后提交或回滚。
  • @DaveG 我将prepare移到了lopp之外,但它只适用于第一次迭代
【解决方案2】:

您可以执行以下操作,

  • 使用[NSSting StringWithFormat:@"Insert Statement with Parameters"]创建查询

  • 将查询存储到数组中。

  • 创建一个事务。你可以通过SQL Query

  • 循环数组并执行查询。

  • 提交交易

.

对于每个插入语句,sqlite 都会开始一个事务并提交它。那是沉重的。为了避免这样的开销,我们可以开始我们的事务。它非常非常快。

【讨论】:

    【解决方案3】:

    最大的瓶颈是api调用。

    最佳实践解决方案是,在 NSOperationQueue 中使用 NSOperation 分隔调用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-05
      • 2015-09-15
      • 2021-12-18
      • 2020-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多