Sqlite3数据库表内数据批量读取操作---sqlite3_stmt机制

article/2025/7/16 21:23:58

0、引言

        在前面两篇文章已经对数据环境搭建、数据批量写入库中进行了较为详细的讲解。因此,基于前两篇文章内容的基础上,本文主要从数据库中批量数据读取操作进行梳理讲解。

        嵌入式数据库SQLite 3配置使用详细笔记教程_sqlite3-CSDN博客

        SQLite 3 优化批量数据存储操作---事务transaction机制_sqlite3 事务-CSDN博客

        数据库,顾名思义,是一个数据存储的容器、装置,既能存储用户的数据,也能将存储的数据读取出来处理。但是,要怎么做才能把数据读取出来又能保证效率呢?基于这个问题,本文将对sqlite3_stmt机制进行说明,并实现数据的读取。

1、sqlite3_stmt机制

        sqlite3_stmt 是 SQLite 数据库在 C 语言接口中使用的一个关键数据结构,它代表了一个“准备语句对象”(prepared statement object),也是一个预编译的 SQL 语句,预编译 SQL 语句可以提高执行效率并防止 SQL 注入攻击。这个对象是对 SQL 语句的一种封装,该 SQL 语句已经被编译成字节码形式,可以直接由 SQLite 的虚拟机执行。

        在 SQLite 中,预编译 SQL 语句有如下的几个优点

①、性能提升:预编译的语句可以被数据库引擎更快地执行,因为编译步骤已经在准备阶段完成了。

②、安全性:使用预编译语句可以有效防止 SQL 注入攻击,因为参数值在执行时是分开绑定的,不会与 SQL 代码混合。

③、减少内存占用:对于重复执行的 SQL 语句,预编译可以减少内存的使用,因为相同的 SQL 代码只需要编译一次。

        使用sqlite3_stmt基本步骤如下:

(1)、打开 SQLite 数据库连接(使用 sqlite3_open() 函数)。

(2)、准备 SQL 语句(使用 sqlite3_prepare_v2() 或 sqlite3_prepare16_v2() 函数)。

(3)、绑定参数(如果有的话,使用 sqlite3_bind_*() 系列函数)。

(4)、执行 SQL 语句(使用 sqlite3_step() 函数)。

(5)、处理结果集(如果有的话,使用 sqlite3_column_*() 系列函数)。

(6)、重置或清理 SQL 语句(使用 sqlite3_reset() 或 sqlite3_finalize() 函数)。

(7)、关闭数据库连接(使用 sqlite3_close() 函数)。

        如果只是读取一条数据时,使用sqlite3_exec和sqlite3_stmt效率是一样的,但是当涉及到大批量的操作时,此前使用的sqlite3_exec就会不是最优选择了,因此推荐使用sqlite3_stmt机制。

2、常用API说明

        从上图所示的Sqlite3的官方网站查询的数据信息可以看到,sqlite3_stmt相关的API有50多个。因此本文这一部分将对sqlite3_stmt常用的API进行介绍。

①、sqlite3_prepare_v2

int sqlite3_prepare_v2(sqlite3 *db,            /* 数据库连接 */const char *zSql,       /* SQL 文本 */int nByte,              /* SQL 文本的最大长度,-1 表示自动计算长度 */sqlite3_stmt **ppStmt,  /* 输出参数,指向预编译语句对象的指针 */const char **pzTail      /* 输出参数,指向 SQL 文本中未处理部分的指针 */
);//成功返回SQLITE_OK (0),失败返回错误码

        sqlite3_prepare_v2() 函数的主要用途是将 SQL 文本编译成一个可以执行的预编译语句对象。预编译语句在执行前可以进行参数绑定,这样可以提高执行效率并防止 SQL 注入攻击。这个函数是 sqlite3_prepare() 的改进版本,提供了更多的功能和更好的错误报告。

②、sqlite3_step

int sqlite3_step(sqlite3_stmt *pStmt);
//pStmt:一个指向预编译语句对象(sqlite3_stmt)的指针。//返回值:
//SQLITE_ROW(1):如果执行成功并且结果集中还有更多的行。
//SQLITE_DONE(100):如果执行成功并且所有行都已经被处理完毕。
//如果发生错误,返回错误码

        sqlite3_step用于执行一个预编译的 SQL 语句,这个函数会执行语句,并将结果集推进到下一行(如果有的话)。当所有行都被处理完毕时,sqlite3_step() 返回 SQLITE_DONE。

③、sqlite3_bind_text

int sqlite3_bind_text(
sqlite3_stmt *stmt,  /*stmt:一个指向预编译语句对象(sqlite3_stmt)的指针。*/
int idx,             /*idx:占位符的索引,从 1 开始计数。*/
const char *zData,   /*zData:要绑定的文本数据的指针。*/
int nData,           /*nData:文本数据的长度(以字节为单位)。如果设置为 -1,SQLite 会自动计算文本的长度。*/
void (*xDel)(void *) /*一个可选的删除函数,用于在 sqlite3_finalize() 调用时释放 zData 指向的内存。如不需要释放内存,可以传递SQLITE_STATIC 或 NULL。*/
);//成功返回SQLITE_OK (0),失败返回错误码

        sqlite3_bind_text用于将文本类型的参数绑定到一个预编译的 SQL 语句(sqlite3_stmt)中的占位符。这个函数允许安全地将变量值插入到 SQL 语句中,从而避免 SQL 注入攻击。

④、sqlite3_column_int64

int64_t sqlite3_column_int64(sqlite3_stmt *stmt, int iCol);
//stmt:一个指向预编译语句对象(sqlite3_stmt)的指针。
//iCol:要获取的列的索引,从 0 开始计数。//成功返回指定列的 int64_t 类型的值,失败返回0

        sqlite3_column_int64用于从结果集中获取一个 int64_t 类型的列值。这个函数通常与 sqlite3_step() 函数一起使用,用于遍历查询结果。

⑤、sqlite3_column_text

const unsigned char *sqlite3_column_text(sqlite3_stmt *stmt, int iCol);
//stmt:一个指向预编译语句对象(sqlite3_stmt)的指针。
//iCol:要获取的列的索引,从 0 开始计数。//成功,返回指向指定列文本值的指针,失败返回NULL

        sqlite3_column_text用于从结果集中获取一个文本类型的列值。这个函数通常与 sqlite3_step() 函数一起使用,用于遍历查询结果。

        提醒:返回的指针指向的是 SQLite 内部的数据结构,不能直接修改或释放这部分内存当 sqlite3_stmt 对象被销毁(例如通过调用 sqlite3_finalize())时,这部分内存也会被自动释放

⑥、sqlite3_column_double

double sqlite3_column_double(sqlite3_stmt *stmt, int iCol);
//stmt:一个指向预编译语句对象(sqlite3_stmt)的指针。
//iCol:要获取的列的索引,从 0 开始计数。//成功,返回返回指定列的 double 类型的值,失败返回0.0

        sqlite3_column_double用于从结果集中获取一个double类型的列值。这个函数通常与 sqlite3_step() 函数一起使用,用于遍历查询结果。

⑦、sqlite3_finalize

int sqlite3_finalize(sqlite3_stmt *stmt);
//stmt:一个指向预编译语句对象(sqlite3_stmt)的指针。//成功返回SQLITE_OK (0),失败返回错误码

        用于清理和释放一个预编译的 SQL 语句对象(sqlite3_stmt)。

3、程序示例

        在编写此参考程序示例时,使用的是之前存储下来的一个数据库中的一个表,该表的格式如下:

        根据上图所示的内容,可以看到,该表中一共有6个数据,3种数据类型,分别是integer、text和real类型。接下来将对表中的数据进行读取解析并可视化呈现出来。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sqlite3.h>typedef struct {long long store_time;char name[20];double detect_time;float x;float y;float z;
}user_data_t;/*** @brief  读取Sqlite数据库中的xxx数据* @param  db:数据库文件描述符* @param  data:存储读取出来的数据* @retval 成功返回实际读取的数据个数,失败返回-1*/
int sqlite3_read_xxx_data(sqlite3* db, user_data_t* data, int max_count)
{if (db == NULL) {DBUG_SHOW(DBUG_ERROR, "%s Variable db is NULL!\n", __FUNCTION__);return -1;}char sql_cmd[] = "SELECT * FROM detect;";int ret = 0;sqlite3_stmt* stmt = NULL;const char* err_msg = NULL;ret = sqlite3_prepare_v2(db, sql_cmd, -1, &stmt, NULL);if (ret != SQLITE_OK) {DBUG_SHOW(DBUG_ERROR, "Prepare statement err: %s\n", sqlite3_errmsg(db));return -1;}int count = 0;while ((ret = sqlite3_step(stmt)) == SQLITE_ROW && count < max_count) {data[count].store_time = sqlite3_column_int64(stmt, 0);strncpy(data[count].name, (const char*)sqlite3_column_text(stmt, 1), sizeof(data[count].name) - 1);data[count].name[sizeof(data[count].name) - 1] = '\0';data[count].detect_time = sqlite3_column_double(stmt, 2);data[count].x = sqlite3_column_double(stmt, 3);data[count].y = sqlite3_column_double(stmt, 4);data[count].z = sqlite3_column_double(stmt, 5);count++;}if (ret != SQLITE_DONE) {err_msg = sqlite3_errmsg(db);if (strcmp(err_msg, "another row available") == 0) {DBUG_SHOW(DBUG_WARN, "This table has another row data available!\n");}else {DBUG_SHOW(DBUG_ERROR, "Fetch data err: %s\n", err_msg);sqlite3_finalize(stmt);return -1;}}sqlite3_finalize(stmt);return count;
}int main(void)
{sqlite3* read_db = NULL;char filename[256] = "2024081214134452.db";database_init(&read_db, filename);detect_database_t* data = malloc(sizeof(detect_database_t) * 512);int ret = sqlite3_read_xxx_data(read_db, data, 512);printf("data num : %d\n", ret);for (int i = 0; i < ret; i++) {printf(".......................................\n");printf("store_time:%lld\n", data[i].store_time);printf("name:%s\n", data[i].name);printf("detect_time:%.3f\n", data[i].detect_time);printf("x:%.3f\n", data[i].x);printf("y:%.3f\n", data[i].y);printf("z:%.3f\n", data[i].z);}free(data);while (1);
}

4、程序效果

        在读取数据库前,通过SQLite Studio程序查看了数据库中,共存放了211条有效数据。

        运行程序后,从可以看到,读取出了211条数据,且经过对比核验,读出的数据与库中的数据保持一致。


http://www.hkcw.cn/article/cnXadLrAUS.shtml

相关文章

官方指定Jmeter配置JVM堆内存方式

软件测试资料领取&#xff1a;[内部资源] 想拿年薪40W的软件测试人员&#xff0c;这份资料必须领取~ 软件测试面试刷题工具领取&#xff1a;软件测试面试刷题【800道面试题答案免费刷】 1.概述 在使用Jmeter做性能测试过程中&#xff0c;可能会应为默认设置的堆内存值较小出…

线上JVM OOM问题,如何排查和解决?

今天咱们来聊聊让无数 Java 开发者头疼的 JVM OOM&#xff08;Out Of Memory&#xff0c;内存溢出&#xff09;问题。在面试中&#xff0c;OOM 问题也是面试官的“心头好”&#xff0c;因为它能直接考察你对 JVM 的理解&#xff0c;以及你在实际问题面前的排查和解决能力。 一…

JVM常见线上问题:CPU 100%、内存泄露问题排查

一、CPU 100% 问题排查 1.1、找到 cpu 占有率最高的 java 进程号 使用命令: top -c 显示运行中的进程列表信息, shift + p 使列表按 cpu 使用率排序显示。 PID = 2227 的进程,cpu 使用率最高 1.2、根据进程号找到 cpu 占有率最高的线程号 使用命令: top -Hp {pid} ,同…

JVM 一文详解

目录 JVM 简介 JVM 中的内存区域划分 1. 堆&#xff08;一个进程只有一份 ------ 线程共享&#xff09; 2. 栈&#xff08;一个进程可以有 N 份 ------ 线程私有&#xff09; Java 虚拟机栈&#xff1a; 本机方法栈&#xff1a; 3. 程序计数器&#xff08;一个线程可以…

【JVM】关于JVM的内部原理你到底了解多少(八股文面经知识点)

前言 &#x1f31f;&#x1f31f;本期讲解关于HTTPS的重要的加密原理~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &#x1f386;那么废话不…

深入理解 JVM 的栈帧结构

🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程,高并发设计,Springboot和微服务,熟悉Linux,ESXI虚拟化以及云原生Docker和K8s…

JVM 机制

目录 一、什么是 JVM&#xff1a; 二、JVM 的运行流程&#xff1a; 三、JVM 内存区域划分&#xff1a; 1、( 1 ) 程序计数器&#xff1a; 1、( 2 ) 元数据区&#xff1a; 1、( 3 ) 栈&#xff1a; 1、( 4 ) 堆&#xff1a; 四、类加载&#xff1a; 1、什么时候会触…

【JVM】类加载机制

文章目录 类加载机制类加载过程1. 加载2. 验证3. 准备4. 解析偏移量符号引用和直接引用 5. 初始化 类加载机制 类加载指的是&#xff0c;Java 进程运行的时候&#xff0c;需要把 .class 文件从硬盘读取到内存&#xff0c;并进行一些列的校验解析的过程&#xff08;程序要想执行…

【JVM】从零开始深度解析JVM

本篇博客给大家带来的是JVM的知识点, 重点在类加载和垃圾回收机制上. &#x1f40e;文章专栏: JavaEE初阶 &#x1f680;若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动力 . 王子,公主请阅&#x1f680; …

一篇文章带你解决笔试面试中的jvm问题

JVM内存区域划分 JVM启动的时候,会申请到一整个很大的内存区域.JVM是一个应用程序,要从操作系统里申请内存.JVM就根据需要,把空间分为几个部分,每个部分各自有不同的功能.具体划分如下: 分为&#xff1a;栈&#xff0c;堆&#xff0c;程序计数器&#xff0c;元数据区 Heap(堆):…

通义灵码2.5——基于编程智能体开发Wiki多功能搜索引擎

引言 在智能化浪潮重塑软件开发范式的今天&#xff0c;我借助开发一个基于编程智能体开发Wiki 多功能搜索引擎&#xff0c;深度体验了通义灵码2.5这一阿里云旗舰级AI编码助手&#xff0c;构建智能协作新范式。 该平台通过三大技术突破赋能开发全流程&#xff1a;基于编程智能…

基于SpringBoot的商家销售管理网站的设计与实现

湖南软件职业技术大学 本科毕业设计(论文) 设计(论文)题目 基于SpringBoot的商家销售管理网站的设计与实现 学生姓名 学生学号 所在学院 专业班级 校内指导教师 企业指导教师 毕业设计(论文)真实性承诺及声明 学生对毕业设计(论文)真实性承诺 本人郑重声明:所提交的毕…

各种噪声电流激励下电源PDN网络对系统时钟性能的影响

点击上面“蓝字”关注我们 电源分配是支持所有类型硅产品运行的基础设施的重要组成部分&#xff0c;但在设计过程中常被忽视。电源质量可能限制电路性能&#xff0c;并决定其工作可靠性。要真正解决电源分配问题&#xff0c;必须考虑包括芯片、封装和PCB在内的整个系统。 芯片…

【大模型】情绪对话模型项目研发

一、使用框架&#xff1a; Qwen大模型后端Open-webui前端实现使用LLamaFactory的STF微调数据集&#xff0c;vllm后端部署&#xff0c; 二、框架安装 下载千问大模型 安装魔塔社区库文件 pip install modelscope Download.py 内容 from modelscope import snapshot_downlo…

关于位图Bitmaps的介绍

目录 1、基本概念 1.1、介绍 1.2、关键字 1.3、结构原理 2、常用命令 2.1、SETBIT 2.2、GETBIT 2.3、BITCOUNT 2.4、BITOP 2.5、BITPOS 3、应用场景 4、使用示例 前言 Redis的Bitmaps是一种基于字符串的数据结构&#xff0c;用于处理位级别的操作。 Bitmaps在Redis…

【软件设计】通过软件设计提高 Flash 的擦写次数

目录 0. 个人简介 && 授权须知1. Flash 和 EEROM 基本情况2. 场景要求3. 软件设计思路4. 代码展示4.1 flash.h4.2 flash.c 0. 个人简介 && 授权须知 &#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是喜欢记录零碎知识点的菜鸟…

C 语言练习--初级

#学习C 代码&#xff0c; 做小练习时&#xff0c;自己运行代码竟然发现很多错误&#xff0c;记录一下。 1、计算器 根据输入的数值和符合&#xff0c;输出相应结果。 结果&#xff1a; #include "stdio.h"int Primary_math(int a, int b, char sign){int num0;swit…

pikachu通关教程-CSRF XSS

XSS XSS漏洞原理 XSS被称为跨站脚本攻击&#xff08;Cross Site Scripting&#xff09;&#xff0c;由于和层叠样式表&#xff08;Cascading Style Sheets&#xff0c;CSS&#xff09;重名&#xff0c;改为XSS。主要基于JavaScript语言进行恶意攻击&#xff0c;因为js非常灵活…

E. Melody 【CF1026 (Div. 2)】 (求欧拉路径之Hierholzer算法)

E. Melody 思路 将所有出现过的音量和音高看作一个点&#xff0c;一个声音看作一条边&#xff0c;连接起来。那么很容易知道要找的就是图上的一条欧拉路径&#xff08;类似一笔画问题&#xff09; 又已知存在欧拉路径的充要条件为&#xff1a;度数为奇数的点的个数为0或者2个…

历年中国科学技术大学计算机保研上机真题

2025中国科学技术大学计算机保研上机真题 2024中国科学技术大学计算机保研上机真题 2023中国科学技术大学计算机保研上机真题 在线测评链接&#xff1a;https://pgcode.cn/school?classification1 拆分数字 题目描述 给定一个数字&#xff0c;拆分成若干个数字之和&#xff…