2011-11-10 82 views
7

我正在使用FMDB在iPhone上創建SQLite數據庫。我有一個initial.sql是形式的FMDB中沒有運行多個查詢

CREATE TABLE Abc ... ; 
CREATE TABLE Def ... ; 

我通過加載文件到一個NSString並運行它

NSString * str = // string from file initial.sql 

[db executeUpdate: str]; 

這成功,但後來我得到一個失敗加載此:

no such table: Def 

很明顯,第二條語句沒有被調用。我怎樣才能做到這一點,以便所有的查詢都會被調用?

據SQLite的文檔: 「的程序sqlite3_prepare_v2(),sqlite3_prepare(),sqlite3_prepare16(),sqlite3_prepare16_v2(),sqlite3_exec(),和sqlite3_get_table()接受一個SQL語句列表(SQL-語句列表)這是一個用分號分隔的語句列表。「

所以,這應該都有效。

+0

見https://github.com/ccgus/fmdb/issues/59 – luqmaan

回答

8

我也被這個咬了;我花了整整一個上午的時間瀏覽FMDatabase並閱讀sqlite3 API文檔以找到它。我仍然不完全確定問題的根源,但根據this bug in PHP,有必要調用sqlite3_exec,而不是使用sqlite3_prepare_v2準備語句,然後調用sqlite3_step。

該文檔似乎並沒有表明會發生這種行爲,因此我們感到困惑,並且我會喜歡有更多sqlite經驗的人提出一些假設。

我通過開發一種執行一批查詢的方法解決了這個問題。請找到下面的代碼。如果你願意,你可以將它重寫成一個類別,而不是將它添加到你的調用FMDatabase.h中。

在FMDatabase.h這給FMDatabase接口地址:

- (BOOL)executeBatch:(NSString*)sql error:(NSError**)error; 

在FMDatabase.m這給FMDatabase實施地址:

- (BOOL)executeBatch:(NSString *)sql error:(NSError**)error 
{ 
    char* errorOutput; 
    int responseCode = sqlite3_exec(db, [sql UTF8String], NULL, NULL, &errorOutput); 

    if (errorOutput != nil) 
    { 
     *error = [NSError errorWithDomain:[NSString stringWithUTF8String:errorOutput] 
            code:responseCode 
           userInfo:nil]; 
     return false; 
    } 

    return true; 
} 

請注意,還有很多功能從缺則ExecuteBatch這使其不適用於許多目的。特別是,它不檢查數據庫是否被鎖定,它不能確保FMDatabase本身沒有被鎖定,它不支持語句緩存。

如果您需要這一點,以上是您自己編寫代碼的好起點。快樂黑客!

+0

我最終確定我的批量查詢是在不同的行和分裂的n ewline字符。然後,我用BEGIN/COMMIT將它包圍起來,使它成爲一個單獨的事務。 – George

2
Split Batch Statement 
Add in .h file: 
#import "FMSQLStatementSplitter.h" 
#import "FMDatabaseQueue.h" 

FMSQLStatementSplitter can split batch sql statement into several separated statements, then [FMDatabase executeUpdate:] or other methods can be used to execute each separated statement: 

FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:databasePath]; 
NSString *batchStatement = @"insert into ftest values ('hello;');" 
          @"insert into ftest values ('hi;');" 
          @"insert into ftest values ('not h!\\\\');" 
          @"insert into ftest values ('definitely not h!')"; 
NSArray *statements = [[FMSQLStatementSplitter sharedInstance] statementsFromBatchSqlStatement:batchStatement]; 
[queue inDatabase:^(FMDatabase *adb) { 
    for (FMSplittedStatement *sqlittedStatement in statements) 
    { 
     [adb executeUpdate:sqlittedStatement.statementString]; 
    } 
}]; 
+0

這個「額外」已被替換爲'sqlite3_exec'的本地包裝器。只需調用'executeStatements'。 – Rob

5

FMDB V2.3現在有sqlite3_exec本地包裝稱爲executeStatements

BOOL success; 

NSString *sql = @"create table bulktest1 (id integer primary key autoincrement, x text);" 
       "create table bulktest2 (id integer primary key autoincrement, y text);" 
       "create table bulktest3 (id integer primary key autoincrement, z text);" 
       "insert into bulktest1 (x) values ('XXX');" 
       "insert into bulktest2 (y) values ('YYY');" 
       "insert into bulktest3 (z) values ('ZZZ');"; 

success = [db executeStatements:sql]; 

它也有一個變種,採用sqlite3_exec回調,作爲一個塊來實現:

sql = @"select count(*) as count from bulktest1;" 
     "select count(*) as count from bulktest2;" 
     "select count(*) as count from bulktest3;"; 

success = [db executeStatements:sql withResultBlock:^int(NSDictionary *dictionary) { 
    NSInteger count = [dictionary[@"count"] integerValue]; 
    NSLog(@"Count = %d", count); 
    return 0; // if you return 0, it continues execution; return non-zero, it stops execution 
}]; 
+0

我想大多數人都沒有意識到,如果你返回非零,它將停止。此外,我認爲最好讓你的街區決定你是否要停下來。正如我在這裏建議的https://github.com/ccgus/fmdb/issues/428 – Qiulang

+0

我想這可能是有人可能不會注意到的,但它在Xcode的快速幫助中記錄了此方法。我不確定你的意思是「最好讓你的塊決定你是否要停止或不停止」,但是:這就是'return 0'的目的,就是讓它繼續。如果你返回一個非零值,它將停止。 – Rob