sqlite3_prepare_v2 / sqlite3_exec
几个关于sqlite3的问题:
什么时候需要使用第一种方法,何时使用另一种方法? 这是他们之间的区别?
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){}
先谢谢你
-
应该经常检查SQLite函数的返回值,以确保它成功,因此使用
if
语句是非常优选的。 如果失败,可以调用sqlite3_errmsg()
来获取错误的Cstring描述。 -
在任何情况下,可以使用
sqlite3_prepare_v2
(而不是sqlite3_exec
):-
一个是返回数据,因此将调用
sqlite3_step
后面跟着一个或多个sqlite3_column_xxx
函数,为每一行数据重复该过程; 要么 -
一个是绑定值的
?
在sqlite3_bind_xxx
的SQL占位符。
从上面可以推断,只有当(a)SQLstring没有参数时才会使用
sqlite3_exec
; 和(b)SQL不返回任何数据。sqlite3_exec
更简单,但只能在这些特定情况下使用。请注意:关于
?
占位符是非常重要的:人们应该避免手动构buildSQL语句(例如,使用stringWithFormat
或Swiftstring插值),特别是如果插入值包括最终用户input。 例如,如果您使用用户input创build的INSERT
,UPDATE
或DELETE
语句调用sqlite3_exec
(例如,将用户提供的某些值插入到数据库中),那么您很可能会遇到未引用引号转义符号等等。一个也暴露在SQL注入攻击。例如,如果
commentString
是作为用户input的结果提供的,则这是不可取的: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)); } if (sqlite3_bind_text(statement, 1, [commentString UTF8String], -1, NULL) != SQLITE_OK) { NSLog(@"Bind 1 failure: %s", sqlite3_errmsg(database)); } if (sqlite3_step(statement) != SQLITE_DONE) { NSLog(@"Step failure: %s", sqlite3_errmsg(database)); } sqlite3_finalize(statement);
请注意,如果这个正确的实现感觉像是太多的工作,你可以使用FMDB库 ,它可以简化为:
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) { ... }
或者更好,如果你想做适当的error handling,你可以这样做:
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 ,在大多数情况下,您需要validation结果是否等于SQLITE_OK以确保您的命令成功运行。 ( SQLITE_OK是inttypes**)。 因此,第二个是优选的。
对于问题2 ,函数sqlite3_exec用于运行任何不返回数据的命令,包括更新,插入和删除。 从数据库中检索数据只涉及一点点。 而sqlite3_prepare_v2函数可以用于SELECT (在SQL
)。 通常情况下, 创build表经常使用第一个。
对于问题3 ,好吧, 当是为了循环 ,而如果是为了条件 。 一般来说,如果你从db中获取dada,你需要一个循环来遍历* return数组**。 如果你插入一个数据到数据库(例如),你可以使用SQLITE_DONE来检查你的操作。
顺便说一下,在大多数情况下, 核心数据在IOS中是首选。
对于我刚发现的#2的迟到答案:当sql_stmt_getIdRecepteur
实际上包含多个SQL语句时, sql_stmt_getIdRecepteur
使用sqlite3_exec
(或在循环中使用sqlite3_prepare_v2
)。 从文档sqlite3_prepare_v2
:
这些例程仅编译zSql中的第一条语句,所以* pzTail指向的是未编译的内容。
sqlite3_exec
包含一个内部循环,多次调用sqlite3_prepare_v2
,直到整个inputstring被编译。 如果不使用sqlite3_exec
,并且在一个string中有多个SQL语句,则需要检查sqlite3_prepare_v2
的pzTail
返回值。