sqlite3_prepare_v2 / sqlite3_exec

时间:2014-12-09 16:18:13

标签: ios objective-c sqlite

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

提前谢谢

3 个答案:

答案 0 :(得分:16)

  1. 应始终检查SQLite函数的返回值,以确保它成功,因此最优选使用if语句。如果失败,可以调用sqlite3_errmsg()来检索错误的C字符串描述。

  2. 在任何情况下都会使用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_execINSERT或使用用户输入创建的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界面的简单性。

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

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

  4. 使用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}}返回值。