• 玩转SQLite-11:C语言高效API之sqlite3_prepare系列函数


    SQLite是一个跨平台的轻量级数据库,支持C/C++开发,可用于嵌入式中,关于C/C++使用SQLite的简单实例,之前这篇文章,已经介绍过一种简单的使用方式。本篇来介绍另一种更加高效的调用方式。

    1.1 普通方式

    之前的文章介绍过sqlite3的C语言API函数基础操作,通过sqlite3_exec函数即可执行sql语句函数,该函数指定一个 sql语句字符串和对应的回调函数。

    当执行sqlite3_exec时,其内部的执行可分为3步:

    • 解析sql语句字符串
    • 编译sql语句
    • 执行sql语句

    可以看到,sqlite3_exec一个函数就实现了这么多功能,这是它的优点——使用方便,但同时也是它的缺点——效率低,因为解析和编译都是比较耗时的。

    关于sqlite3_exec的使用示例可参考之前的文章:玩转SQLite6:使用C语言来读写数据库

    1.2 高效方式

    为此解决sqlite3_exec函数执行效率低的问题,就出现了其它更加高效的解决方式:将sqlite3_exec的功能进行分解,由多个函数共同完成。这就是本篇要介绍的:

    • **sqlite3_prepare_v2()**函数:实现对sql语句(模板)的解析和编译,生成了可以被执行的 sql语句实例
    • **sqlite3_stmt()**数据结构:可以理解为一种“准备语句对象”,它可以结合变量使用,进而实现相同操作的循环
    • **sqlite3_bind_*() **函数:用于绑定赋值变量
    • **sqlite3_step() **函数:用于执行sql语句

    相比较使用sqlite3_exec函数,现在这种方式,sql语句的解析和编译只执行了一次,而sqlite3_step执行多次,整体的效率势必大大提升。

    2 函数介绍

    2.1 sqlite3错误码

    在介绍各个函数之前,先来看一个这些函的错误码有哪些

    #define SQLITE_OK           0   /* 成功 */
    /* 错误码 */
    #define SQLITE_ERROR        1   /* sql错误或丢失的数据库,SQL error or missing database */
    #define SQLITE_INTERNAL     2   /* sqlite内部逻辑错误,Internal logic error in SQLite */
    #define SQLITE_PERM         3   /* 拒绝访问,Access permission denied */
    #define SQLITE_ABORT        4   /* 回调函数请求取消操作,Callback routine requested an abort */
    #define SQLITE_BUSY         5   /* 数据库文件被锁定,The database file is locked */
    #define SQLITE_LOCKED       6   /* 数据库的一个表被锁定,A table in the database is locked */
    #define SQLITE_NOMEM        7   /* 某次malloc函数调用失败,A malloc() failed */
    #define SQLITE_READONLY     8   /* 尝试写入一个只读数据库,Attempt to write a readonly database */
    #define SQLITE_INTERRUPT    9   /* 操作sqlite3_interrupt函数被中断,Operation terminated by sqlite3_interrupt()*/
    #define SQLITE_IOERR       10   /* 发生磁盘I/O错误,Some kind of disk I/O error occurred */
    #define SQLITE_CORRUPT     11   /* 数据库磁盘映像不正确,The database disk image is malformed */
    #define SQLITE_NOTFOUND    12   /* 找不到表或记录,NOT USED. Table or record not found */
    #define SQLITE_FULL        13   /* 数据库满而插入失败,Insertion failed because database is full */
    #define SQLITE_CANTOPEN    14   /* 无法打开数据库,Unable to open the database file */
    #define SQLITE_PROTOCOL    15   /* 数据库锁定协议错误,NOT USED. Database lock protocol error */
    #define SQLITE_EMPTY       16   /* 数据库为空,Database is empty */
    #define SQLITE_SCHEMA      17   /* 数据库结构发生改变,The database schema changed */
    #define SQLITE_TOOBIG      18   /* 数据大小超限,String or BLOB exceeds size limit */
    #define SQLITE_CONSTRAINT  19   /* 约束违反,Abort due to constraint violation */
    #define SQLITE_MISMATCH    20   /* 数据类型不匹配,Data type mismatch */
    #define SQLITE_MISUSE      21   /* 库使用不正确,Library used incorrectly */
    #define SQLITE_NOLFS       22   /* 使用了操作系统不支持的功能,Uses OS features not supported on host */
    #define SQLITE_AUTH        23   /* 授权失败,Authorization denied */
    #define SQLITE_FORMAT      24   /* 附加数据库格式错误,Auxiliary database format error */
    #define SQLITE_RANGE       25   /* sqlite3_bind的第2给参数超出范围,2nd parameter to sqlite3_bind out of range */
    #define SQLITE_NOTADB      26   /* 不是数据库文件,File opened that is not a database file */
    #define SQLITE_NOTICE      27   /* Notifications from sqlite3_log() */
    #define SQLITE_WARNING     28   /* Warnings from sqlite3_log() */
    #define SQLITE_ROW         100  /* sqlite3_step产生一个就绪行,sqlite3_step() has another row ready */
    #define SQLITE_DONE        101  /* sqlite3_step执行完成,sqlite3_step() has finished executing */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    2.2 sqlite3_prepare_v2

    该函数实现对sql语句(模板)的解析和编译,生成了可以被执行的sql语句实例

    int sqlite3_prepare_v2(
        sqlite3 *db,            /* Database handle */
        const char *zSql,       /* SQL statement, UTF-8 encoded */
        int nByte,              /* Maximum length of zSql in bytes. */
        sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
        const char **pzTail     /* OUT: Pointer to unused portion of zSql */
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    参数

    • db:sqlite数据库
    • zSql:要执行的sql语句(可以包含未赋值的变量)
    • nByte:sql语句的(字符串的)长度
    • ppStmt:解析编译出的sql语句实例
    • pzTail:

    返回值:见前面的sqlite3错误码

    2.3 sqlite3_bind

    该函数组用于绑定变量值到prepare语句中,也就是给 sqlite3_stmt变量赋值。前面的文章讲过,我们一定是先通过sqlite3_prepare_v2函数创建并初始化一个 sqlite3_stmt 变量语句,然后使用sqlite3_bind_xxx函数对 这个 sql语句变量进行绑定参数。

    int sqlite3_bind_int(sqlite3_stmt*, int, int);
    int sqlite3_bind_doubule(sqlite3_stmt*, int, double);
    int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int, void(*)(void*));
    
    • 1
    • 2
    • 3

    参数

    • 形参1:sqlite3_stmt: prepare语句编译出的sql语句实例
    • 形参2: sqlite3_stmt变量参数的序号索引值,规定最左侧的SQL参数的索引值为 1,也就是说参数索引值从1开始。
    • 形参3: 是要绑定给第2个形参指向的 变量参数的 实际值。第2个形参可以指向不同的索引值。
    • 形参4: 对于有4个形参的函数,第4个形参一般是第3个形参的长度。
    • 形参5: 是用于BLOB和字符串绑定后的 析构函数,用于在sqlite处理完blob或字符串之后处理它,一般可以设置为NULL。

    返回值:见前面的sqlite3错误码

    代码示例:
    假设表的字段结构为:person(name,age,sex),数据库指针为 pdb。

        sqlite3_stmt *pstmt;
    	const char *sql = "INSERT INTO person(name, age, sex) VALUES(?,?,?);";
    	nRet = sqlite3_prepare_v2(pdb, sql, strlen(sql), &pstmt, &pzTail);
    	int i;
     
    	for(i = 0; i < 10; i++){
    		nCol = 1;
    		sqlite3_bind_text(pstmt, nCol++, a[i].name, strlen(a[i].name), NULL);
    		sqlite3_bind_int(pstmt, nCol++, a[i].age);
    		sqlite3_bind_text(pstmt, nCol++, a[i].sex, strlen(a[i].name), NULL);
     
    		sqlite3_step(pstmt);
    		sqlite3_reset(pstmt);
    	}
     
    	sqlite3_finalize(pstmt);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2.4 sqlite3_step

    int sqlite3_step(sqlite3_stmt *pStmt);
    
    • 1

    参数

    • pStmt:prepare语句编译出的sql语句实例

    返回值:

    这里再对几个常见的返回值进一步说明:

    • SQLITE_DONE:意味着sql语句执行完成且成功。一旦执行成功后,sqlite3_step()就不应该被再次调用执行,除非我们使用sqlite3_reset()重置 sqlite3_stmt 数据。

    • SQLITE_ROW:这个比较常用,当我们的sql语句是 读命令,比如"SELECT FROM…",返回的数据一般很多,并且数据是按行返回的,且每次只返回一行*,其返回值为 SQLITE_ROW,所以需要重复调用sqlite3_step函数,直到sqlite3_step返回 SQLITE_DONE.

    • SQLITE_MISUSE: 表示该函数实例被滥用,不合适,比如sqlite_stmt结构已经被销毁了。

    2.5 sqlite3_reset

    int sqlite3_reset(sqlite3_stmt *pStmt);
    
    • 1

    用于重置一个准备语句对象到它的初始状态,然后准备被重新执行。所有sql语句变量使用sqlite3_bind*绑定值,使用sqlite3_clear_bindings重设这些绑定。Sqlite3_reset接口重置准备语句到它代码开始的时候。sqlite3_reset并不改变在准备语句上的任何绑定值,那么这里猜测,可能是语句在被执行的过程中发生了其他的改变,然后这个语句将它重置到绑定值的时候的那个状态。

    2.6 sqlite3_column

    该函数实例用于 查询(query)结果的筛选,返回当前结果的某1列。

    int sqlite3_column_int(sqlite3_stmt*, int iCol);
    double sqlite3_column_double(sqlite3_stmt*, int iCol);
    const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
    
    • 1
    • 2
    • 3

    参数

    • sqlite3_stmt*:prepare语句编译出的sql语句实例

    • iCol: 要查询的"列"索引值。sqlite3规定最左侧的“列”索引值是 0,也就是“列”索引号从 0 开始。

    返回

    • 根据函数类型,返回相应的数据,比如int型,double型(浮点数也是),text(字符串型)等。

    3 总结

    本篇主要介绍了sqlite的C语言操作的高效API函数,用于取代功能强大但效率较低的sqlite3_exec函数。本篇介绍到的几个API函数总结如下:

    • sqlite3_prepare_v2() 创建sqlite3_stmt对象
    • sqlite3_bind_*() 绑定参数值到sqlite3_stmt
    • sqlite3_step() 运行sql语句,可以是一次,也可以是循环执行
    • sqlite3_reset() 重置sqlite3_stmt对象
    • sqlite3_finalize() 销毁sqlite3_stmt对象

    下篇,将通过一个实际的例子,来体会这些函数的具体使用效果。

  • 相关阅读:
    Leetcode(695)——岛屿的最大面积
    SpringBoot中 Mybatis 的xml映射文件配置
    目标检测算法之损失函数(IOU、GIOU、DIOU、CIOU、EIOU)
    为什么图片传输要使用base64编码
    【项目设计】自主HTTP服务器
    基于Java的家政服务预约平台设计与实现(源码+lw+部署文档+讲解等)
    12.OpenFeign 实例(springcloud)
    web前端学习笔记10
    HTTP版本、状态码
    Linux驱动开发(一)---环境搭建与hello world
  • 原文地址:https://blog.csdn.net/hbsyaaa/article/details/127858034