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

2024-08-23 22:12

本文主要是介绍Sqlite3数据库表内数据批量读取操作---sqlite3_stmt机制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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条数据,且经过对比核验,读出的数据与库中的数据保持一致。

这篇关于Sqlite3数据库表内数据批量读取操作---sqlite3_stmt机制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1100632

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

异构存储(冷热数据分离)

异构存储主要解决不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。 异构存储Shell操作 (1)查看当前有哪些存储策略可以用 [lytfly@hadoop102 hadoop-3.1.4]$ hdfs storagepolicies -listPolicies (2)为指定路径(数据存储目录)设置指定的存储策略 hdfs storagepolicies -setStoragePo

Hadoop集群数据均衡之磁盘间数据均衡

生产环境,由于硬盘空间不足,往往需要增加一块硬盘。刚加载的硬盘没有数据时,可以执行磁盘数据均衡命令。(Hadoop3.x新特性) plan后面带的节点的名字必须是已经存在的,并且是需要均衡的节点。 如果节点不存在,会报如下错误: 如果节点只有一个硬盘的话,不会创建均衡计划: (1)生成均衡计划 hdfs diskbalancer -plan hadoop102 (2)执行均衡计划 hd

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

sqlite3 相关知识

WAL 模式 VS 回滚模式 特性WAL 模式回滚模式(Rollback Journal)定义使用写前日志来记录变更。使用回滚日志来记录事务的所有修改。特点更高的并发性和性能;支持多读者和单写者。支持安全的事务回滚,但并发性较低。性能写入性能更好,尤其是读多写少的场景。写操作会造成较大的性能开销,尤其是在事务开始时。写入流程数据首先写入 WAL 文件,然后才从 WAL 刷新到主数据库。数据在开始