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){} 

先谢谢你

  1. 应该经常检查SQLite函数的返回值,以确保它成功,因此使用if语句是非常优选的。 如果失败,可以调用sqlite3_errmsg()来获取错误的Cstring描述。

  2. 在任何情况下,可以使用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的INSERTUPDATEDELETE语句调用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接口的简单性。

  3. 当检索多行数据时,可以使用:

     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_OKinttypes**)。 因此,第二个是优选的。

对于问题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_v2pzTail返回值。