iOS-5中Sqlite的性能问题

7
我在使用iOS 5中的sqlite时遇到一个问题。我从Menu.db表中获取两个表中的记录:一个是Recipe,另一个是Ingredients。从Recipe表中获取所有记录,并根据一个recipeid从ingredients表中获取记录。当在iOS 4.2上运行时,获取记录需要很少的时间,但是在iOS 5上运行时,获取记录需要花费更长的时间。请查看以下代码:
NSString *query = [NSString stringWithFormat:@"select id from Recipes"];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, [query UTF8String], -1, &selectstmt, NULL) == SQLITE_OK) {
    while(sqlite3_step(selectstmt) == SQLITE_ROW) {
        rcp.recipeID = sqlite3_column_int(selectstmt, 0);
        NSString *sql = [NSString stringWithFormat:@"select Name from Ingredients where recipeId = %d",rcp.recipeID];
        sqlite3_stmt *stmt2;
        if(sqlite3_prepare_v2(database, [sql UTF8String], -1, &stmt2, NULL) == SQLITE_OK) {
            while(sqlite3_step(stmt2) == SQLITE_ROW) {}
        }
    }
}

为什么iOS 5.0出现了这个问题,相同的代码在iOS 4.0、4.2上运行良好?

我知道,我编写的代码是正确的,我想知道在iOS 5.0中出现Sqlite性能问题的确切原因,因为我的应用程序完全基于数据库构建。


在该项目中是否创建了2个 .sql 文件? - Nimit Parekh
我怀疑数据库存在差异。(你是否验证了在两种情况下是否获得相同的结果?) - Hot Licks
你也应该尝试使用键/值数据库(如LevelDB或TokyoCabinet),现在更改可能需要付出很大的努力,但速度非常快。 - JP Illanes
@Raspu:你能提供一些关于LevelDB和TokyoCabinet的链接吗?我想获取正确的信息。 - Rupesh
我会用它来回答,稍等一下。 - JP Illanes
显示剩余6条评论
6个回答

1

尝试使用两个不同的函数

在完成第一个查询的完整执行后,开始执行第二个查询。

例如:

NSString *query = [NSString stringWithFormat:@"select id from Recipes"];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, [query UTF8String], -1, &selectstmt, NULL) == SQLITE_OK) {
    while(sqlite3_step(selectstmt) == SQLITE_ROW) {
        rcp.recipeID = sqlite3_column_int(selectstmt, 0);
    }
}

然后调用

 NSString *sql = [NSString stringWithFormat:@"select Name from Ingredients where recipeId = %d",rcp.recipeID];
            sqlite3_stmt *stmt2;
            if(sqlite3_prepare_v2(database, [sql UTF8String], -1, &stmt2, NULL) == SQLITE_OK) {
                while(sqlite3_step(stmt2) == SQLITE_ROW) {}

希望这能帮助解决你的问题。

1

如果您想将.db文件转换为.sqlite文件

打开您的.db文件,选择表格 文件-> 导出--> 以CSV格式导出表格(保存文件时使用.csv格式) (这样您就可以选择所有表格了) 然后打开.sqlite文件 文件-> 导入--> 从CSV导入表格 在选择.csv文件后,会弹出一个对话框 在其中,“从第一行提取字段名称”必须被选中 现在您的sqlite文件已经准备好了。

将此文件放入您的项目中

然后设置您的.sqlite/.db文件路径

现在按照以下方式设置您的选择查询

#import <sqlite3.h>


    -(void)SelectSqlData:(NSString *)SearchString
    {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDirectory = [paths objectAtIndex:0];
        NSString *path = [documentsDirectory stringByAppendingPathComponent:@"yourfileName.sqlite"];
        sqlite3_stmt *compiledStatement;
        sqlite3 *database;
        if(sqlite3_open([path UTF8String], &database) == SQLITE_OK) {

            const char *sqlStatement;

            sqlStatement = "select c.field1,c.field2,c.field3,c.field4 from YourTableName1 as c,  YourTableName2 as b  where b.Artist_Id = ?"; 

            sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL);

            //printf("\nError===%s",sqlite3_errmsg(database));


            if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {

                sqlite3_bind_text(compiledStatement,1,[SearchString UTF8String] , -1,SQLITE_STATIC);

                while(sqlite3_step(compiledStatement) == SQLITE_ROW ) 

                {

                        NSString *str_field1=[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 0)];

                        NSString *str_field2=[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];

                        NSString *str_field3=[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];

                        NSString *str_field4=[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)];


    // add str_field into array


            }

            }

            sqlite3_finalize(compiledStatement);


        }


        sqlite3_close(database);
    }

1

这可能不是你要找的答案,但以下是一个小技巧来提高性能。

NSString *query = [NSString stringWithFormat:@"select id from Recipes"];
sqlite3_stmt *selectstmt;

if(sqlite3_prepare_v2(database, [query UTF8String], -1, &selectstmt, NULL) == SQLITE_OK) {
    while(sqlite3_step(selectstmt) == SQLITE_ROW) {
        rcp.recipeID = sqlite3_column_int(selectstmt, 0);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //NSString *sql = [NSString stringWithFormat:@"select Name from Ingredients where recipeId = %d",rcp.recipeID];//
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        sqlite3_stmt *stmt2;
        if(sqlite3_prepare_v2(database, [sql UTF8String], -1, &stmt2, NULL) == SQLITE_OK) {
            while(sqlite3_step(stmt2) == SQLITE_ROW) {}
        }
    }
}

在每次while循环迭代中,您都会创建一个新的NSString对象(NSString *sql = ...),因此也许您应该这样做:

NSString *query = [NSString stringWithFormat:@"select id from Recipes"];
NSString *sql = [NSString stringWithFormat:@"select Name from Ingredients where recipeId = %d",rcp.recipeID];
sqlite3_stmt *selectstmt;

if(sqlite3_prepare_v2(database, [query UTF8String], -1, &selectstmt, NULL) == SQLITE_OK) {
    while(sqlite3_step(selectstmt) == SQLITE_ROW) {
        rcp.recipeID = sqlite3_column_int(selectstmt, 0);
        sqlite3_stmt *stmt2;
        if(sqlite3_prepare_v2(database, [sql UTF8String], -1, &stmt2, NULL) == SQLITE_OK) {
            while(sqlite3_step(stmt2) == SQLITE_ROW) {}
        }
    }
}

希望这能有所帮助!


1

我认为你链接了libsqlite3.dylib库。你应该链接libsqlite3.0.dylib库。


我已经使用了 libsqlite3.0.dylib。 - Rupesh

1

GetListBySQL函数已经进行了优化,且不受iOS版本限制。希望能对您有所帮助。

-(NSMutableArray*)GetListBySQL:(NSString*)SQL
{
NSMutableArray* Array;

Array=[[NSMutableArray alloc]init];
NSStringEncoding enc = [NSString defaultCStringEncoding];

sqlite3_stmt *select_statement=nil;

if (sqlite3_prepare_v2(database, [SQL UTF8String], -1, &select_statement, NULL) != SQLITE_OK) {
    NSString *errString = [NSString stringWithFormat:@"%@", [@"Fail" stringByReplacingOccurrencesOfString:@"#" withString:[NSString stringWithCString:sqlite3_errmsg(database) encoding:enc] ]];
    NSAssert1(0, @"%@", errString);
}

int columncount=sqlite3_column_count(select_statement);

NSMutableDictionary* dic;

while (sqlite3_step(select_statement) == SQLITE_ROW) 
{   
    dic=[[NSMutableDictionary alloc]init];

    for(int j=0;j<columncount;j++)
    {
        if(sqlite3_column_text(select_statement, j)!=nil)
            [dic setObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(select_statement, j)] forKey:[NSString stringWithUTF8String:(char *)sqlite3_column_name(select_statement,j)]];
        else
            [dic setObject:@"" forKey:[NSString stringWithUTF8String:(char *)sqlite3_column_name(select_statement,j)]];
    }

    [Array addObject:dic];
    [dic release];      
}

sqlite3_finalize(select_statement);

NSMutableArray *arr = [[NSMutableArray alloc] initWithArray: Array];

[Array release];

return arr;
}

0

另一种选择是将SQLite更改为像LevelDB(来自Google)或TokyoCabinet这样的键/值数据库。我现在正在使用LevelDB进行两个项目,效果非常好,过去我也使用过TokyoCabinet,但问题是它是LGPL,所以我不确定它是否与iOS环境完全兼容,但无论如何,我有几个应用程序在Appstore中使用了Tokyo Cabinet(不要告诉苹果)。

要同时使用它们,您需要一个包装器(或者您可以开发自己的包装器)。这是一个快速比较和可用的包装器:

  • LevelDB:它似乎是最快的之一(如果不是最快的话,请看他们的基准测试)。作为封装,我目前正在使用NULevelDB,如果您在将其添加到项目中遇到任何问题,请告诉我(我遇到过一些问题)。

  • TokyoCabinet:它似乎没有LevelDB快(我没有运行测试,因为许可证问题而放弃了它),但在官方页面上,他们推荐使用他们的新库KyotoCabinet,我还没有测试过,但应该更快。我使用的封装是由了不起的Aaron Hillegass制作的,称为BNRPersistence

作为建议,可以尝试使用LevelDB,因为它有一个更大的社区支持,并且其包装器(NULevelDB)简单友好。
祝你好运!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接