数据库sqlite3之 sqlite3_exec()第三个参数回调函数的使用

在写这篇文章之前大家先了解我之前写的关于用c语言操作sqlite3的博客,链接地址如下:
https://blog.csdn.net/makunIT/article/details/105192076
关于sqlite3_exec的回调函数的知识,我也是在做一个项目中学习到的,看了一些博客吧,很多博客,都表达的不是很清楚,所以我想写这篇博客,记录自己的学习过程。大家先了解一下sqlite3_exec()函数吧。

1、sqlite3_exec()

函数原型

#include <sqlite3.h>

int sqlite_exec(sqlite  *db, 
				const char *sql,
 				int (*callback)(void *,int,char **,char **),
				void *,
 				char **errmsg);

函数说明:用来执行sql语句,查询的结果返回给回调函数callback。
参数说明
第一个参数:db是用于保存打开的数据库文件dbname的信息;
第二个参数:sql你要执行命令的语句;
第三个参数:callback,回调函数,用来处理查询结果,如果不需要回调(比如做insert 或者delete 操作时),可以输入NULL;
第四个参数:void *是你所提供的指针,你可以传递任何一个指针参数到这里,这个参数最终会传到回调函数里面,如果不需要传递指针给回调函数,可以填NULL;
第五个参数:errmsg,返回错误信息,注意是指针的指针。

返回值:执行成功返回SQLITE_OK,否则返回其他值

说明:通常,sqlite3_callback和它后面的void*这两个位置都可以填NULL。填NULL表示你不需要回调。比如你做insert 操作,做delete操作,就没有必要使用回调。而当你做select 时,就要使用回调,因为sqlite3 把数据查出来,得通过回调告诉你查出了什么数据。虽然回调显得代码整齐,但有时候你还是想要非回调的select查询。这可以通过sqlite3_get_table 函数做到。

那么了解了以上sqlite3_exec()函数,那么我下面将讲解一下sqlite_exec()的参数回调函数callback

2、sqlite3_exec()的回调函数callback
在了解callback之前,我们先了解一下什么是回调函数:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数;
那么在sqlite3_exec中,我们应该这样理解先执行*sql对应的功能命令,然后将结果传递给回调函数,回调函数根据结果再进一步执行。这代表着,这个 “回调函数”才是最有意义的,我们要讲我们需要的功能,通过回调函数来实现,不管是获取数据库表中有效信息,还是其他动作。

函数原型:

typedef int(*sqlite_callback)(void* para,
							 int columenCount,
 							 char** columnValue, 
 							 char** columnName);

函数说明:由用户处理查询的结果;
参数说明
第一个参数:para: 由sqlite3_exec传入的参数指针,或者说是指针参数;
第二个参数:columnCount: 查询到的这一条记录由多少个字段(多少列);
第三个参数:columnValue : 查询出来的数据都保存在这里,它实际上是个1 维数组(不要以为是2 维数组),每一个元素都是一个char * 值,是一个字段内容(用字符串来表示,以‘\0’结尾);
第四个参数:columnName : 该参数是双指针,语columnValue是对应的,表示这个字段的字段名称;
返回值:执行成功返回SQLITE_OK,否则返回其他值

说明回调函数多数时候不是执行1次,而是会循环执行n次,当我们使用select进行sql功能时,往往输出的结果会是 多行,那么 有n行,就会执行n次的 回调函数

下面我将会写两段代码,来实现回调函数,一种是直接用insert直接插入到db表中,一种是用snprintf()函数来实现向表中插入数据的。大家先来看第一种吧

第一种的代码:

/*********************************************************************************
 *      Copyright:  (C) 2020 makun<1394987689@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  sqlite_delete.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(2020年03月27日)
 *         Author:  makun <1394987689@qq.com>
 *      ChangeLog:  1, Release initial version on "2020年03月27日 00时47分05秒"
 *                 
 ********************************************************************************/

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sqlite3.h>
#include <stdlib.h>

static int callback(void *data, int argc, char **argv, char **azColName);

int main (int argc, char **argv)
{
    sqlite3   *db=NULL;
    int       rc ;
    char      *zerrmsg=0;
    char      *sql;
    char      *data= "callback function called";

 //1、创建数据库,如果存在则打开
/***********************************************************************************************************************/
    rc = sqlite3_open("sqlite.db",&db);
    if(rc)
    {
        printf("create sqlite failure:%s\n",sqlite3_errmsg(db));
        exit(1);
    }

    printf("create sqlite successfuly\n");

//2、创建数据库表
/***********************************************************************************************************************/
    sql = "CREATE TABLE COMPANY("  \
                            "ID INT PRIMARY KEY     NOT NULL," \
                             "NAME          TEXT    NOT NULL," \
                             "AGE           INT     NOT NULL," \
                             "ADDRESS       CHAR(50)," \
                            "SALARY         REAL );";

    rc = sqlite3_exec(db,sql,callback, 0, &zerrmsg);
    if( rc !=SQLITE_OK)
    {
        printf("SQL error!:%s\n",zerrmsg);
        sqlite3_free(zerrmsg);
    }
    printf("create table COMPANY successfuly\n");

//3、向表中插入数据
/***********************************************************************************************************************/
    sql="INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) "  \
                             "VALUES (1, 'Paul', 32, 'California', 20000.00 ); " \
                            "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) "  \
                             "VALUES (2, 'Allen', 25, 'Texas', 15000.00 ); "     \
                             "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY)" \
                             "VALUES (3, 'Teddy', 23, 'Norway', 20000.00 );";

    rc=sqlite3_exec(db,sql,callback,(void *)data,&zerrmsg);
    if(rc !=SQLITE_OK)
    {
        printf("SQL error!:%s\n",zerrmsg);//打印错误信息
        sqlite3_free(zerrmsg);//释放掉zerrmsg的内存空间
    }
    else
    {
        printf("create company  successfuly\n");
    }

//4、查询表中的数据
/**********************************************************************************************************************/
    sql = "SELECT * from COMPANY";
    rc = sqlite3_exec(db, sql, callback, (void*)data, &zerrmsg);
    if( rc != SQLITE_OK )
    {
        printf("SQL error: %s\n", zerrmsg);
        sqlite3_free(zerrmsg);
    }
    else
    {
        printf( "Select from COMPANY successfully\n");
    }
    sqlite3_close(db);//关闭数据库
    return 0;
}
//5、查询表中的数据时用到的回调函数
/*********************************************************************************************************************/
static int callback(void *data, int argc, char **argv, char **azColName)
{
    int i;
    printf( "%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;
}
/**********************************************************************************************************************/

程序结果如下:
在这里插入图片描述

可以看出来,由于sql命令行为 select* from COMPANY,该命令会将表中所有信息都输出,总共5个字段(列),包含3条信息(行),所以这个回调函数会被执行3次,如上式结果理解这个逻辑,非常重要。

第二种代码:

/*********************************************************************************
 *      Copyright:  (C) 2020 makun<1394987689@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  sqlite_insert.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(2020年03月27日)
 *         Author:  makun <1394987689@qq.com>
 *      ChangeLog:  1, Release initial version on "2020年03月27日 00时47分05秒"
 *                 
 ********************************************************************************/


#include <stdio.h>
#include <string.h>
#include <sqlite3.h>
#include <stdlib.h>
#pragma pack(1)

typedef struct tlv_data
{
    unsigned    char sn[12];
    unsigned    char temp[7];
    unsigned    char datatime[11];
    int  id;            
}tlv_send_data;
#pragma pack()

static int callback(void *ptr, int argc, char **argv, char **azCoName);
void dump_buf(char *data, int len);
//int delete_db(sqlite3 *db);

int main (int argc, char **argv)
{
    sqlite3   *db=NULL;
    int       rc = -1;
    char      *zerrmsg=0;
    char      *sql;
    char      *sql2;
    char      *sql3;
    int       rd;
    char     sn[12]={0xfd,0x01,12,52,50,49,30,30,30,31,61,0};
    char     temp[7]={0xfd,0x02,7,17,0,52,0xf9};
    char     datatime[11]={0xfd,0x03,11,78,5,13,15,1,28,0x4e,0x2a};
    tlv_send_data  read_db_t;

    dump_buf(sn, 12);//以十六进制输出
    dump_buf(temp,7);
    dump_buf(datatime,11);

//1创建数据库
/*******************************************************************************************************************/
    rc = sqlite3_open("data.db",&db);
    if(rc < 0)
    {
        printf("create data failure:%s\n",sqlite3_errmsg(db));
        return -1;
    }

    else
    {
        printf("create data successfuly\n");
    }
//2创建数据库的表格
/********************************************************************************************************************/
    if(sqlite3_exec(db,"create table if not exists temperature(id INTEGER PRIMARY KEY AUTOINCREMENT,sn varchar,temp varchar,datatime varchar);",NULL,NULL,&zerrmsg)!=SQLITE_OK)
    {
        printf("Create failed:%s\n",zerrmsg);
        return -1;
    }

    printf("Open table temperature successfully!\n");
//3向表格中插入数据
/************************************************************************************************************************/

    char insert[1024];
    memset(insert,0, sizeof(insert));
    //将三个数组中的字节存放到数据库中,以字符串格式存储
    snprintf(insert,sizeof(insert),"insert into temperature values(null,'%s','%s','%s');",sn,temp,datatime);
    if((sqlite3_exec(db, insert, NULL, NULL,&zerrmsg) != SQLITE_OK ))
    {   
        printf("sql error:%s\n",zerrmsg);
        sqlite3_free(zerrmsg);
    }   


    printf("Insert successfuly\n");
//4读取数据库中表中的数据 ,只读取数据表中一行数据
/************************************************************************************************************************/
    memset(&read_db_t,0,sizeof(read_db_t));
    read_db(db, &read_db_t);
   // delete_db(db);
    return 0;
}

//5读取数据库中表中的数据,只读取表中一行数据
/***********************************************************************************************************/
int  read_db(sqlite3 *db,tlv_send_data *pp)
{

    char *zErrmsg=NULL;
    int   rc; 
    char *sql2="select *from temperature limit 1 ";

    rc=sqlite3_exec(db, sql2,callback, (void *)pp,&zErrmsg);
    if(rc !=SQLITE_OK)
    {   
        printf("sql error:%s\n", zErrmsg);
        sqlite3_free(zErrmsg);
    }   
    else
    {   
        printf("select from temperature successfuly\n");
    }   

    return  0;  
}
//6读取数据时用到的回调函数
/********************************************************************************************************************/
static int callback(void *ptr, int argc, char **argv, char **azCoName)
{
    int i;

    tlv_send_data *data =(tlv_send_data *)ptr;//将ptr强制转换为tlv_send_data,然后存放获取第一条数据的信息,然后返回给参数pp

    for(i=0;i <argc; i++)
    {
        printf("%s = %s\n",azCoName[i],argv[i]?argv[i]:"NULL" );
        if(!strcmp("id",azCoName[i]))
        {
            data->id=atoi(argv[i]);
        }
        else if(!strcmp("sn",azCoName[i]))
        {
            strcpy(data->sn,argv[i]);
            dump_buf(data->sn,12);
        }
        else if(!strcmp("temp",azCoName[i]))
        {
            strcpy(data->temp,argv[i]);
            dump_buf(data->temp,7);

        }
        else if(!strcmp("datatime",azCoName[i]))
        {
            strcpy(data->datatime,argv[i]);
            dump_buf(data->datatime,11);
        }
    }
        printf("\n");

        return 0;
 
}
//以十六进制输出数组中的数据
/************************************************************************************************************************/
void dump_buf( char *data, int len)//data指针,指向buf的首地址,len是buf的长度
{
    int i;
    for(i=0; i<len; i++)
    { 
        printf("0x%02x ",(unsigned char)data[i]);
        if( 0 == (i+1)%16 )
         printf("\n");
    }   
        printf("\n");
}
/**********************************************************************************************************************/

/*int delete_db(sqlite3 *db)
{
    char *zErrmsg =NULL;
    char *sql3="DELETE FROM temperature where id =(SELECT id from temperature limit 1)";

    if((sqlite3_exec(db, sql3, NULL, NULL,&zErrmsg) != SQLITE_OK ))
    {   
        printf("sql error:%s\n",zErrmsg);
        sqlite3_free(zErrmsg);
    }  
    printf("delect from temperature successfuly\n");
    return 0;
}*/

在这里插入图片描述
分析:在上式中我将三个数组中的字节按照%s的格式存放在数据库中,当我用回调函数读取数据库中的数据时,我们会发现,图片中打印的是乱码,printf函数以参数"%s"输出字符串时过程为:
(1)从首地址开始逐字节寻址,把存储单元(一个字节)内的数据转换为ASCII字符格式输出。
(2)直到某一个字节内存的元素为字符’\0’时,输出此字符并且寻址结束。
字符数组里没有’\0’,因此使用printf %s 输出时,可能会因为没有结束的’\0’而多输出一些乱码或是字符串。如果要正确的输出字符数组,最好一位一位输出。
另外根据上面的情况我们知道,callback函数,第三个参数:columnValue : 查询出来的数据都保存在这里,它实际上是个1 维数组(不要以为是2 维数组),每一个元素都是一个char * 值,是一个字段内容(用字符串来表示,以‘\0’结尾);
而我们在定义temp时temp[4]=0,temp[5]=52,temp[6]=f9但是当我们用回调函数打印时我们会惊奇的发现的temp[5]=0;temp[6]=0,这个数据出现的问题就是callback的第三个参数引起的,它将查询的数据用char *保存的,(用字符串来表示,以’\0‘结尾),我在做项目的时候就发现了这一点,刚开始看的时候关于这个细节没有把握住,以至于花了自己一点时间,但是对于犯错,我们并不可怕,可怕的是遇到错误我们就放弃了。

在这里插入图片描述在这里插入图片描述

  • 8
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
`sqlite3_prepare_v2`和`sqlite3_exec`函数都是SQLite数据库的API函数,但在功能和使用方面有所不同。 `sqlite3_prepare_v2`函数是用于准备SQL语句的,它需要三个参数:一个SQLite数据库连接对象,一个SQL语句字符串和一个指向已编译SQL语句的指针。它将SQL语句编译为字节码,但并不执行该语句,因此它通常与`sqlite3_step`函数结合使用,以逐步执行SQL语句。 例如,以下代码演示了如何使用`sqlite3_prepare_v2`和`sqlite3_step`函数来执行SELECT语句: ``` sqlite3_stmt* stmt; const char* sql = "SELECT * FROM mytable"; sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); while (sqlite3_step(stmt) == SQLITE_ROW) { // 处理结果集 } sqlite3_finalize(stmt); ``` 相比之下,`sqlite3_exec`函数可以在一次调用中执行一条完整的SQL语句。它需要四个参数:一个SQLite数据库连接对象,一个SQL语句字符串,一个回调函数和一个回调函数的第一个参数。该回调函数将在每次执行SQL语句时调用,以处理结果集或执行其他自定义操作。 例如,以下代码演示了如何使用`sqlite3_exec`函数来执行INSERT语句: ``` const char* sql = "INSERT INTO mytable (id, name) VALUES (1, 'John')"; sqlite3_exec(db, sql, NULL, NULL, NULL); ``` 总的来说,`sqlite3_prepare_v2`函数比`sqlite3_exec`函数更灵活,因为它允许逐步执行SQL语句,并提供更多的控制和错误处理机制。但是,如果您只需要执行一次简单的SQL语句,则`sqlite3_exec`函数可能更方便。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值