1

我有两个数据库。

  1. ch_coins.db 是 [NSBundle mainBundle] 中 tableview 的只读数据。
  2. 文档中的 User_data.sqlite

我尝试结合从两个数据库中选择并将数据填充到对象。我的方法是这样的:

-(NSMutableArray*)returnSubCountries
{
NSString *path = [[[NSBundle mainBundle] resourcePath]
                  stringByAppendingPathComponent:databaseName];

NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *userDB = [documentsDir stringByAppendingPathComponent:user_data];


NSMutableArray *subCountiresArr=[[NSMutableArray alloc]init];

if (sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{

    /*const char *sqlSubCountries="SELECT subCountryID,subCountryName,subCountryComment,image,priority,hasRegions,navigationKey\
    FROM subCountries\
    ORDER BY priority ASC";*/
    const char *sqlSubCountries="SELECT subCountryID,subCountryName,subCountryComment,image,priority,hasRegions,navigationKey,usdb.quantity\
    attach database 'userDB' as usdb\
    INNER JOIN usdb on subCountries.subCountryID=usdb.refID\
    FROM subCountries\
    ORDER BY priority ASC";
    sqlite3_stmt *statement;
    int sqlResult = sqlite3_prepare_v2(database, sqlSubCountries, -1, &statement, NULL);
    if ( sqlResult== SQLITE_OK)
    {
        while (sqlite3_step(statement) == SQLITE_ROW)
        {
            SubCountry *sbCountryObj=[[SubCountry alloc]init];
            sbCountryObj.subCountryID=sqlite3_column_int(statement, 0);
            char *subCountryName=(char *)sqlite3_column_text(statement, 1);
            char *subCountryComment=(char *)sqlite3_column_text(statement, 2);
            char *image=(char *)sqlite3_column_text(statement, 3);
            sbCountryObj.priority=sqlite3_column_int(statement, 4);
            sbCountryObj.hasRegions=(sqlite3_column_int(statement, 5)==1);
            sbCountryObj.navigationKey=sqlite3_column_int(statement, 6);
            sbCountryObj.quantity=sqlite3_column_int(statement, 7);
            sbCountryObj.subCountryName=(subCountryName)?[NSString stringWithUTF8String:subCountryName]: @"";
            sbCountryObj.subCountryComment=(subCountryComment)?[NSString stringWithUTF8String:subCountryComment]: @"";
            sbCountryObj.image=(image)?[NSString stringWithUTF8String:image]: @"";
            [subCountiresArr addObject:sbCountryObj];
        }
        sqlite3_finalize(statement);
        sqlite3_close(database);
    }
}
else
{
    //[self dbConnectionError];
}
return subCountiresArr;
}

我一定做错了什么。任何帮助深表感谢。谢谢。

编辑:

-(NSMutableArray*)returnSubCountries
{
NSString *path = [[[NSBundle mainBundle] resourcePath]
                  stringByAppendingPathComponent:databaseName];

NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *userDB = [documentsDir stringByAppendingPathComponent:user_data];


NSMutableArray *subCountiresArr=[[NSMutableArray alloc]init];

if (sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{

    /*const char *sqlSubCountries="SELECT subCountryID,subCountryName,subCountryComment,image,priority,hasRegions,navigationKey\
    FROM subCountries\
    ORDER BY priority ASC";*/
    NSString *userDBName = [NSString stringWithFormat:@"attach database '%@' as usdb", userDB];
    const char *sqlAttachedDatabase = [userDBName UTF8String];

    const char *sqlSubCountries="SELECT subCountryID,subCountryName,subCountryComment,image,priority,hasRegions,navigationKey,usdb.quantity\
    sqlAttachedDatabase userDB as usdb\
    INNER JOIN usdb on subCountries.subCountryID=usdb.refID\
    FROM subCountries\
    ORDER BY priority ASC";
    sqlite3_stmt *statement;
    int sqlResult = sqlite3_prepare_v2(database, sqlSubCountries, -1, &statement, NULL);
    if ( sqlResult== SQLITE_OK)
    {
        while (sqlite3_step(statement) == SQLITE_ROW)
        {
            SubCountry *sbCountryObj=[[SubCountry alloc]init];
            sbCountryObj.subCountryID=sqlite3_column_int(statement, 0);
            char *subCountryName=(char *)sqlite3_column_text(statement, 1);
            char *subCountryComment=(char *)sqlite3_column_text(statement, 2);
            char *image=(char *)sqlite3_column_text(statement, 3);
            sbCountryObj.priority=sqlite3_column_int(statement, 4);
            sbCountryObj.hasRegions=(sqlite3_column_int(statement, 5)==1);
            sbCountryObj.navigationKey=sqlite3_column_int(statement, 6);
            sbCountryObj.quantity=sqlite3_column_int(statement, 7);
            sbCountryObj.subCountryName=(subCountryName)?[NSString stringWithUTF8String:subCountryName]: @"";
            sbCountryObj.subCountryComment=(subCountryComment)?[NSString stringWithUTF8String:subCountryComment]: @"";
            sbCountryObj.image=(image)?[NSString stringWithUTF8String:image]: @"";
            [subCountiresArr addObject:sbCountryObj];
        }
        sqlite3_finalize(statement);
        sqlite3_close(database);
    }
    else
    {
        NSLog(@"%s: prepare failed: %s", __FUNCTION__, sqlite3_errmsg(database));
    }
}
else
{
    //[self dbConnectionError];
}
return subCountiresArr;
}
4

2 回答 2

1

您应该执行以下操作:

  1. 运行 SQL "attach database 'full db path' as usdb",其中 "full db path" 是数据库文件的完整路径
  2. 准备并执行 SELECT 语句(从中删除“附加数据库 ...”)
  3. 不需要时分离数据库“分离数据库'完整数据库路径'”
于 2013-03-22T13:58:10.287 回答
1

You're specifying the database path as userDB in your SQL, but that's not the name of the database file. That is, the name of the variable that has the full path name in it, but you presumably actually have to build your SQL with that filename. For example:

NSString *sql = [NSString stringWithFormat:@"attach database '%@' as usdb", userDB];
const char *sqlAttachedDatabase = [sql UTF8String];

Execute that. Then, as a separate statement, you can execute your SELECT SQL that uses the usdb alias. And when you're done, detach the database.

Also, I notice that you're checking to see if the result is SQLITE_OK (which is good). But you're not showing any diagnostic information if it's not. For example, your SQL is wrong, but you're not showing a meaningful error message. Thus, if your sqlite3_prepare_v2 fails to return SQLITE_OK, you should:

NSLog(@"%s: prepare failed: %s", __FUNCTION__, sqlite3_errmsg(database));

If you do this when you have an error, you'll be able to figure out what's wrong. Without that, you're flying blind.


A working example, where my "author" database is in the bundle, and my "book" database is in Documents:

int rc;

NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *bookFilename = [docsPath stringByAppendingPathComponent:@"book.sqlite"];

NSString *authorFilename = [[NSBundle mainBundle] pathForResource:@"author" ofType:@"sqlite"];

sqlite3 *database;

if ((rc = sqlite3_open([authorFilename UTF8String], &database)) != SQLITE_OK)
{
    NSLog(@"%s: open failed: %s (%d)", __FUNCTION__, sqlite3_errmsg(database), rc);
    return;
}

NSString *sql = [NSString stringWithFormat:@"attach database '%@' as userdb;", bookFilename];

if ((rc = sqlite3_exec(database, [sql UTF8String], NULL, NULL, NULL)) != SQLITE_OK)
    NSLog(@"%s: attach failed: %s (%d)", __FUNCTION__, sqlite3_errmsg(database), rc);

sqlite3_stmt *statement;

sql = @"select book.*, author.* from userdb.book inner join author on author_id = book_author_id;";

if ((rc = sqlite3_prepare_v2(database, [sql UTF8String], -1, &statement, NULL)) != SQLITE_OK)
    NSLog(@"%s: prepare failed: %s (%d)", __FUNCTION__, sqlite3_errmsg(database), rc);

while ((rc = sqlite3_step(statement)) == SQLITE_ROW)
{
    // do whatever you want row by row
    NSLog(@"Row");
}

if (rc != SQLITE_DONE)
    NSLog(@"%s: step failed: %s (%d)", __FUNCTION__, sqlite3_errmsg(database), rc);

sqlite3_finalize(statement);

sql = @"detach database userdb;";

if ((rc = sqlite3_exec(database, [sql UTF8String], NULL, NULL, NULL)) != SQLITE_OK)
    NSLog(@"%s: detach failed: %s (%d)", __FUNCTION__, sqlite3_errmsg(database), rc);

sqlite3_close(database);
于 2013-03-22T14:20:42.680 回答