2011-11-10 13 views
7

Używam FMDB do tworzenia bazy danych SQLite na iPhone. Mam initial.sql że ma postaćWiele pytań nie działa w FMDB

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

załadować to poprzez załadowanie pliku do NSString i uruchomienie go

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

[db executeUpdate: str]; 

to uda, ale później mam awarię:

no such table: Def 

Jest oczywiste, że drugie oświadczenie nie jest wywoływane. Jak mogę to zrobić, aby wszystkie zapytania były wywoływane?

Zgodnie z dokumentacją SQLite: „Procedury sqlite3_prepare_v2(), sqlite3_prepare(), sqlite3_prepare16(), sqlite3_prepare16_v2(), sqlite3_exec() i sqlite3_get_table() przyjmuje listy instrukcji SQL (SQL-stmt-list) która jest rozdzieloną średnikami listą instrukcji. "

To wszystko powinno działać.

+0

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

Odpowiedz

8

Ugryzł mnie także ten; zajęło mi cały ranek przechodzenie przez FMDatabase i czytanie dokumentacji API sqlite3, aby ją znaleźć. Wciąż nie jestem do końca pewny co do głównej przyczyny problemu, ale zgodnie z this bug in PHP, konieczne jest wywołanie sqlite3_exec zamiast przygotowania instrukcji za pomocą sqlite3_prepare_v2, a następnie wywołanie sqlite3_step.

Ta dokumentacja nie wydaje się sugerować, że takie zachowanie mogłoby się wydarzyć, stąd nasze zamieszanie i chciałbym, aby ktoś z większym doświadczeniem z sqlite przedstawił kilka hipotez.

Rozwiązałem to, opracowując metodę wykonywania partii zapytań. Proszę znaleźć kod poniżej. Jeśli wolisz, możesz przepisać to na kategorię zamiast dodawać ją do FMDatabase.h, swojego połączenia.

Dodaj do tego interfejsu FMDatabase w FMDatabase.h:

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

Dodaj to do realizacji FMDatabase w FMDatabase.m:

- (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; 
} 

Należy pamiętać, że istnieje wiele funkcji brakujących od executeBatch które sprawiają, że nie nadaje się do wielu celów. W szczególności nie sprawdza, czy baza danych jest zablokowana, nie upewnia się, że sama biblioteka FMDatabase nie jest zablokowana, nie obsługuje buforowania instrukcji.

Jeśli tego potrzebujesz, powyższe stanowi dobry punkt wyjścia do samodzielnego zakodowania. Szczęśliwy hacking!

+0

Tak naprawdę skończyłem właśnie upewniając się, że moje zapytania były w różnych liniach i dzielone przez n ewlineowy charakter.Następnie otoczyłem go BEGIN/COMMIT, aby zrobić z tego tylko jedną transakcję. – 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

Ten "dodatkowy" został zastąpiony rodzimym opakowaniem w 'sqlite3_exec'. Po prostu wywołaj "executeStatements". – Rob

5

FMDB v2.3 ma teraz natywnego otoki dla sqlite3_exec nazwie 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]; 

Posiada również wariant, który wykorzystuje sqlite3_exec oddzwonienia, realizowany jako blok:

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

Sądzę, że większość ludzi nie zdaje sobie sprawy, że jeśli powrócisz do wartości niezerowej, zatrzyma się. Poza tym, myślę, że lepiej jest pozwolić twojemu blokowi zdecydować, czy chcesz przestać, czy nie. Jak sugeruję tutaj https://github.com/ccgus/fmdb/issues/428 – Qiulang

+0

Myślę, że to możliwe, że ktoś może nie zauważyć, ale jest to udokumentowane w szybkiej pomocy w Xcode dla tej metody. Nie jestem pewien, co masz na myśli mówiąc "lepiej jest pozwolić twojemu blokowi zdecydować, czy chcesz się zatrzymać, czy nie", chociaż: właśnie tym celem jest 'return 0', aby powiedzieć, żeby kontynuował. Jeśli zwrócisz niezerową wartość, zatrzyma się. – Rob