sqlite3_exec() 回调函数说明

sqlite3_exec() Callback function clarification

我无法理解 SQLite3 数据库中回调函数的使用。

我理解是用来遍历SELECT条语句多条记录的。但我不明白它是如何做到的或如何使我自己的 useful 回调。我已经通读 this tutorial 好几遍试图理解,但这对我来说并不合适。

当我使用他们的示例并在 Visual Studio 中进行调试以查看如何填充和遍历参数数组时,我迷路了。另外 VS 只显示数组中的当前槽,而不是整个数组本身。

如果您需要任何说明,请告诉我,因为我是来学习的!

我正在请求某人解释如何使用回调。也许是其他人如何使用它的一些例子。只是解释一下这个人在做什么:

static int callback(void *data, int argc, char **argv, char **azColName){
   int i;
   fprintf(stderr, "%s: ", (const char*)data);
   for(i=0; i<argc; i++){
      printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
   }
   printf("\n");
   return 0;
}

假设您有一个名为 User 的非常简单的 table,看起来像这样:

╔════╦══════════╗
║ ID ║ Name     ║
╟────╫──────────╢
║ 1  ║ Slvrfn   ║
║ 2  ║ Sean     ║
║ 3  ║ Drew     ║
║ 4  ║ mah      ║
╚════╩══════════╝

然后你这样调用sqlite3_exec(参数详细描述in the documentation):

/* Error handling omitted for brevity */
sqlite3_exec(db, "SELECT * FROM User", my_special_callback, NULL, NULL);

SQLite 将执行传递的 SQL 语句,并且对于它找到的每个结果行,它将调用 my_special_callback。因此,对于我们的示例 User table,my_special_callback 将被调用 4 次。所以让我们创建 my_special_callback:

/*
 * Arguments:
 *
 *   unused - Ignored in this case, see the documentation for sqlite3_exec
 *    count - The number of columns in the result set
 *     data - The row's data
 *  columns - The column names
 */
static int my_special_callback(void *unused, int count, char **data, char **columns)
{
    int idx;

    printf("There are %d column(s)\n", count);

    for (idx = 0; idx < count; idx++) {
        printf("The data in column \"%s\" is: %s\n", columns[idx], data[idx]);
    }

    printf("\n");

    return 0;
}

给定我们的示例 table 和数据,输出将如下所示:

There are 2 column(s)
The data in column "ID" is: 1
The data in column "Name" is: Slvrfn

There are 2 column(s)
The data in column "ID" is: 2
The data in column "Name" is: Sean

There are 2 column(s)
The data in column "ID" is: 3
The data in column "Name" is: Drew

There are 2 column(s)
The data in column "ID" is: 4
The data in column "Name" is: mah

现在是如何让它变得有用,这就是 sqlite3_exec 的第 4 个参数的用武之地。来自文档:

The 4th argument to sqlite3_exec() is relayed through to the 1st argument of each callback invocation.

所以假设我们想要 运行 我们的 SQL 并构建我们所有用户姓名的链接列表。我们需要做的第一件事是改变我们调用 sqlite3_exec:

的方式
/* Create my fictional linked list */
struct my_linked_list *head = my_linked_list_alloc();

/*
 * Pass a pointer to my list as the 4th argument to sqlite3_exec. Error
 * handling omitted for brevity
 */
sqlite3_exec(db, "SELECT * FROM User", my_special_callback, head, NULL);

/* My list is now built, I can do stuff with it... */
my_linked_list_traverse(head, /* ... Stuff ... */);

并修改my_special_callback使用

/*
 * Arguments:
 *
 *     list - Pointer to a linked list of names
 *    count - The number of columns in the result set
 *     data - The row's data
 *  columns - The column names
 */
static int my_special_callback(void *list, int count, char **data, char **columns)
{
    struct my_linked_list *head = list;

    /*
     * We know that the value from the Name column is in the second slot
     * of the data array.
     */
    my_linked_list_append(head, data[1]);

    return 0;
}

现在,如果您要使用问题中包含的 callback,您可以这样称呼它:

/*
 * Pass the table name as the 4th argument to sqlite3_exec. Error
 * handling omitted for brevity
 */
sqlite3_exec(db, "SELECT * FROM User", callback, "User", NULL);

输出将是:

User: 
ID = 1
Name = Slvrfn

User: 
ID = 2
Name = Sean

... etc ...

(除了 User: 部分将打印到 stderr 而不是 stdout)

希望这可以帮助您解决问题。如果还有不明白的地方,请告诉我。

那个教程太可怕了,因为它除了 sqlite3_exec().

什么都没用

在一般情况下,唯一使用sqlite3_exec()的有用方法是将其替换为sqlite3_prepare_v2()/sqlite3_step()/sqlite3_column_*()/sqlite3_finalize()调用,以便您可以读取数据您实际需要处理的同一个地方:

sqlite3_stmt *stmt;
const char *sql = "SELECT ID, Name FROM User";
int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
    print("error: ", sqlite3_errmsg(db));
    return;
}
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
    int id           = sqlite3_column_int (stmt, 0);
    const char *name = sqlite3_column_text(stmt, 1);
    // ...
}
if (rc != SQLITE_DONE) {
    print("error: ", sqlite3_errmsg(db));
}
sqlite3_finalize(stmt);