-->

Can iPhone sqlite apps attach to other databases?

2020-07-16 02:44发布

问题:

The ATTACH DATABASE command is useful for transferring rows between sqlite database files and allows you to join rows from tables across databases e.g.

$ sqlite3 BookLoansDB.sqlite 
sqlite> ATTACH DATABASE '/Users/.../Documents/BooksDB.sqlite' AS books_db;
sqlite> select B.BookName, B.Pages, BL.LentTo from main.tblBookLoan BL inner join books_db.tblBook B on B.BookID = BL.BookID;
The Client|512|Jenny
The Pelican Brief|432|Mike

How can I do the same from objective-c on the iPhone. I've had no success with this kind of code:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [paths objectAtIndex:0];
const char *booksDBPath = [[documentDirectory stringByAppendingPathComponent:@"BooksDB.sqlite"] UTF8String];
const char *bookLoansDBPath = [[documentDirectory stringByAppendingPathComponent:@"BookLoansDB.sqlite"] UTF8String];
sqlite3 *bookLoansDB;
int result = sqlite3_open(bookLoansDBPath, &bookLoansDB);
sqlite3_stmt *attachStmt;
NSString *attachSQL = [NSString stringWithFormat: @"ATTACH DATABASE \'%s\' AS books_db", bookLoansDBPath];
result = sqlite3_prepare_v2(bookLoansDB, [attachSQL UTF8String] , -1, &attachStmt, nil);
char *errorMessage;
result = sqlite3_exec(bookLoansDB, [attachSQL UTF8String], NULL, NULL, &errorMessage);
sqlite3_stmt *selectStmt;
NSString *selectSQL = @"select * from main.tblBookLoan BL inner join books_db.tblBook B on B.BookID = BL.BookID";
result = sqlite3_prepare_v2(bookLoansDB, [selectSQL UTF8String] , -1, &selectStmt, nil);
// result == 1
result = sqlite3_step(selectStmt) ; 
// result == 21
if (result == SQLITE_ROW)
{
    //do something
}

Can it even be done?

回答1:

I've got the example working (had my database names mixed up in the "attach database" SQL). So yes it can be done. Thanks for pointing me in the right direction Deepmist

As such examples are rather hard to find, I've pasted the working version below.

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [paths objectAtIndex:0];
const char *booksDBPath = [[documentDirectory stringByAppendingPathComponent:@"BooksDB.sqlite"] UTF8String];
const char *bookLoansDBPath = [[documentDirectory stringByAppendingPathComponent:@"BookLoansDB.sqlite"] UTF8String];
sqlite3 *bookLoansDB;
if (sqlite3_open(bookLoansDBPath, &bookLoansDB) == SQLITE_OK) {
    NSString *attachSQL = [NSString stringWithFormat: @"ATTACH DATABASE \'%s\' AS books_db", booksDBPath];
    char *errorMessage;
    if (sqlite3_exec(bookLoansDB, [attachSQL UTF8String], NULL, NULL, &errorMessage) == SQLITE_OK) {
        sqlite3_stmt *selectStmt;
        NSString *selectSQL = @"select * from main.tblBookLoan BL inner join books_db.tblBook B on B.BookID = BL.BookID";
        if (sqlite3_prepare_v2(bookLoansDB, [selectSQL UTF8String] , -1, &selectStmt, nil) == SQLITE_OK) {
            int n=0;
            while (sqlite3_step(selectStmt) == SQLITE_ROW) {
                //do something
            }
        }
        else {
            NSLog(@"Error while creating select statement: '%s'", sqlite3_errmsg(bookLoansDB));
        }
    }
    else {
        NSLog(@"Error while attaching databases: '%s'", errorMessage);
    }
}
else {
    NSLog(@"Failed to open database at %@ with error %s", booksDBPath, sqlite3_errmsg(bookLoansDB));
    sqlite3_close(bookLoansDB);
}


回答2:

You can attach databases in sqlite on the iPhone. It's hard to say what's going on with your code but it should help if it looks a little more like this:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [paths objectAtIndex:0];
const char *booksDBPath = [[documentDirectory stringByAppendingPathComponent:@"BooksDB.sqlite"] UTF8String];
const char *bookLoansDBPath = [[documentDirectory stringByAppendingPathComponent:@"BookLoansDB.sqlite"] UTF8String];
sqlite3 *bookLoansDB;
if (sqlite3_open(bookLoansDBPath, &bookLoansDB) == SQLITE_OK) {
    NSString *attachSQL = [NSString stringWithFormat: @"ATTACH DATABASE \'%s\' AS books_db", bookLoansDBPath];
    char *errorMessage;
    if (sqlite3_exec(bookLoansDB, [attachSQL UTF8String], NULL, NULL, &errorMessage) == SQLITE_OK && errorMessage == nil) {
        sqlite3_stmt *selectStmt;
        NSString *selectSQL = @"select * from main.tblBookLoan BL inner join books_db.tblBook B on B.BookID = BL.BookID";
        if (sqlite3_prepare_v2(bookLoansDB, [selectSQL UTF8String] , -1, &selectStmt, nil) == SQLITE_OK) {
            while (sqlite3_step(selectStmt) == SQLITE_ROW) {
                //process row
            }
        }
        else {
            NSLog(@"Error while creating select statement: '%s'", sqlite3_errmsg(bookLoansDB));
        }
    }
    else {
        NSLog(@"Error while attaching databases: '%s'", errorMessage);
    }
}
else {
    NSLog(@"Failed to open database at %@ with error %s", booksDBPath, sqlite3_errmsg(bookLoansDB));
    sqlite3_close(bookLoansDB);
}

I haven't tested this code, just modified yours, so it might require fixes.