关于sqlite3的几个问题:
1.何时需要使用第一种方法而另一种方式?它们之间有区别吗?
sqlite3_prepare_v2(_contactDB, sql_stmt_getIdRecepteur, -1, &sqlStatement, NULL);
和
if(sqlite3_prepare_v2(_contactDB, sql_stmt_getIdRecepteur, -1, &sqlStatement, NULL) == SQLITE_OK) {}
2.什么时候最多指示使用'sqlite3_exec'而不是'sqlite3_prepare_v2'?
3.何时需要使用第一个,第二个或第三个:
while(sqlite3_step(sqlStatement) == SQLITE_ROW){}
if(sqlite3_step(sqlStatement) == SQLITE_ROW){}
if(sqlite3_step(sqlStatement) == SQLITE_DONE){}
提前谢谢
答案 0 :(得分:16)
应始终检查SQLite函数的返回值,以确保它成功,因此最优选使用if
语句。如果失败,可以调用sqlite3_errmsg()
来检索错误的C字符串描述。
在任何情况下都会使用sqlite3_prepare_v2
(而不是sqlite3_exec
):
一个人正在返回数据,因此会调用sqlite3_step
后跟一个或多个sqlite3_column_xxx
函数,为每行数据重复该过程;或
一个是使用sqlite3_bind_xxx
将值绑定到SQL中的?
占位符。
从上面可以推断,只有当(a)SQL字符串没有参数时才会使用sqlite3_exec
; (b)SQL不返回任何数据。 sqlite3_exec
更简单,但只应在这些特定情况下使用。
请注意:有关?
占位符的要点非常重要:应该避免手动构建SQL语句(例如,使用stringWithFormat
或Swift字符串插值),尤其是在插入的值包括end时 - 用户输入。例如,如果您使用sqlite3_exec
,INSERT
或使用用户输入创建的UPDATE
语句调用DELETE
(例如,将用户提供的某些值插入数据库) ,你很可能会出现因未转义的引号和转义符号等引起的问题。其中一个也会受到SQL注入攻击。
例如,如果因用户输入而提供了commentString
,那么这是不可取的:
NSString *sql = [NSString stringWithFormat:@"INSERT INTO COMMENTS (COMMENT) VALUES ('%@')", commentString];
if (sqlite3_exec(database, [sql UTF8String], NULL, NULL, NULL) != SQLITE_OK) {
NSLog(@"Insert failure: %s", sqlite3_errmsg(database));
}
相反,你应该:
const char *sql = "INSERT INTO COMMENTS (COMMENT) VALUES (?)";
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) != SQLITE_OK) {
NSLog(@"Prepare failure: %s", sqlite3_errmsg(database));
return;
}
if (sqlite3_bind_text(statement, 1, [commentString UTF8String], -1, SQLITE_TRANSIENT) != SQLITE_OK) {
NSLog(@"Bind 1 failure: %s", sqlite3_errmsg(database));
sqlite3_finalize(statement);
return;
}
if (sqlite3_step(statement) != SQLITE_DONE) {
NSLog(@"Step failure: %s", sqlite3_errmsg(database));
}
sqlite3_finalize(statement);
注意,如果这个正确的实现觉得它太多了,你可以使用FMDB library,这会简化它:
if (![db executeUpdate:@"INSERT INTO COMMENTS (COMMENT) VALUES (?)", commentString]) {
NSLog(@"Insert failure: %@", [db lastErrorMessage]);
}
这提供了sqlite3_prepare_v2
方法的严谨性,但sqlite3_exec
界面的简单性。
检索多行数据时,可以使用:
while(sqlite3_step(sqlStatement) == SQLITE_ROW) { ... }
或者,更好的是,如果你想进行正确的错误处理,你可以这样做:
int rc;
while ((rc = sqlite3_step(sqlStatement)) == SQLITE_ROW) {
// process row here
}
if (rc != SQLITE_DONE) {
NSLog(@"Step failure: %s", sqlite3_errmsg(database));
}
检索单行数据时,可以:
if (sqlite3_step(sqlStatement) != SQLITE_ROW) {
NSLog(@"Step failure: %s", sqlite3_errmsg(database));
}
执行不会返回任何数据的SQL时,可以:
if (sqlite3_step(sqlStatement) != SQLITE_DONE) {
NSLog(@"Step failure: %s", sqlite3_errmsg(database));
}
使用SQLite C界面时,您可以看到正确完成它需要一些工作。这个名为FMDB的接口有一个瘦的Objective-C包装器,它不仅简化了与SQLite数据库的交互,而且更加健壮。
答案 1 :(得分:1)
对于问题1 ,在大多数情况下,您需要验证结果是否等于 SQLITE_OK ,以确保您的命令成功运行。 ( SQLITE_OK 是 int 类型**)。因此,第二个是首选。
对于问题2 ,函数 sqlite3_exec 用于运行任何不返回数据的命令,包括更新,插入和删除。从数据库中检索数据几乎没有涉及。函数 sqlite3_prepare_v2 可用于 SELECT (在SQL
中)。通常, create table 经常使用第一个。
对于问题3 ,好吧,而用于循环,而如果用于条件即可。通常,如果从db检索dada,则需要循环来遍历*返回数组**。如果您将数据插入到db(例如),则可以使用 SQLITE_DONE 来检查操作。
顺便说一句,在大多数情况下,IOS首选核心数据。
答案 2 :(得分:0)
我刚刚找到#2的后期答案:当sqlite3_exec
实际包含多个SQL语句时,使用sqlite3_prepare_v2
(或在循环中使用sql_stmt_getIdRecepteur
)。来自docs for sqlite3_prepare_v2
:
这些例程只编译zSql中的第一个语句,因此* pzTail指向仍未编译的内容。
sqlite3_exec
包含一个内部循环,它会多次调用sqlite3_prepare_v2
,直到整个输入字符串被编译。如果您不使用sqlite3_exec
,并且字符串中包含多个SQL语句,则需要检查pzTail
的{{1}}返回值。