7

我在 iOS 5 中使用 sqlite 时遇到一个问题。我从两个表中获取记录:一个在 Recipe 中,另一个在来自一个 Menu.db 的成分中

从食谱表中我得到所有记录,并recipeid在此基础上从配料表中获取记录。在 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 for Sqlite 的性能问题背后的确切原因,因为我的应用程序完全围绕数据库构建。

4

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) {}

希望这有助于解决您的问题。

于 2012-06-28T09:19:18.867 回答
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) {}
        }
    }
}

希望这个对你有帮助!

于 2012-06-28T23:12:52.703 回答
1

我认为你链接反对libsqlite3.dylib。您应该libsqlite3.0.dylib改为链接库。

于 2012-06-28T11:18:32.323 回答
1

The function GetListBySQL is optimized and iOS versions independent. May it will help you out.

-(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;
}
于 2012-06-29T13:47:56.717 回答
1

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

打开你的 .db 文件 选择表格 File-> Export--> Table from CSV (用 .csv 格式保存你的文件)(像这样选择所有表格)然后打开 .sqlite 文件 File-> Import-> Table from 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);
    }
于 2012-06-28T12:12:06.113 回答
0

另一种选择是将 SQLite 更改为 Key/Value 数据库,例如 LevelDB(来自 google)或 TokyoCabinet。我现在在两个项目中使用LevelDB并且工作得很好,我过去也使用过TokyoCabinet,TokyoCabinet的问题是LGPL,所以我不确定是否与iOS环境完全兼容,但是无论如何,我在应用商店中有几个使用 Tokyo Cabinet 的应用程序(不要告诉 Apple)。

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

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

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

作为推荐,尝试一下LevelDB,背后有一个更大的社区,并且包装器(NULevelDB)简单友好。

祝你好运!

于 2012-07-04T14:40:47.417 回答