多个查询未在 FMDB 中运行
Posted
技术标签:
【中文标题】多个查询未在 FMDB 中运行【英文标题】:Multiple Queries not Running in FMDB 【发布时间】:2011-11-10 00:01:17 【问题描述】:我正在使用 FMDB 在 iPhone 上创建 SQLite 数据库。我有一个initial.sql,格式为
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-stmt-list),它是以分号分隔的语句列表。”
所以,这一切都应该有效。
【问题讨论】:
见github.com/ccgus/fmdb/issues/59 【参考方案1】:我也被这个咬了;我花了整个上午的时间浏览 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 本身没有被锁定,它不支持语句缓存。
如果您需要,上面是您自己编写代码的一个很好的起点。快乐的黑客攻击!
【讨论】:
我实际上只是确保我的批处理查询位于不同的行上并由换行符分割。然后我用 BEGIN/COMMIT 包围它,使其成为一个单一的事务。【参考方案2】: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
];
【讨论】:
我想大多数人都没有意识到,如果你返回非零,它就会停止。此外,我认为最好让你的块来决定你是否要停止。正如我在这里建议的github.com/ccgus/fmdb/issues/428 我猜可能有人没有注意到,但它在 Xcode 的快速帮助中对此方法进行了记录。不过,我不确定您所说的“最好让您的街区决定是否要停止”是什么意思:这就是return 0
的目的,告诉它继续。如果返回一个非零值,它将停止。【参考方案3】:
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];
];
【讨论】:
这个“额外”已被替换为sqlite3_exec
的原生包装器。只需致电executeStatements
。以上是关于多个查询未在 FMDB 中运行的主要内容,如果未能解决你的问题,请参考以下文章