SQLite(嵌入式数据库)
概念:
一种轻量级的关系型数据库管理系统,可以在应用程序中作为一个单独的组件运行,因此也被称为嵌入式数据库。与传统的客户端-服务器架构不同,SQLite 数据库存储在单个文件中,并直接与应用程序交互。它不需要专门的服务器进程或守护进程,也不需要网络通信,因此非常适合用于移动设备、桌面应用程序或其他小型嵌入式环境。
SQLite 使用 C API 进行访问,可以轻松地集成到任何支持 C 语言的环境中。它还提供了多种语言绑定,包括 Python、Java、PHP、Ruby 等,使得在这些语言中使用 SQLite 更加容易。 是一种高效、易用的嵌入式数据库解决方案,特别适合在资源有限的环境中使用。
选择SQLite原因:
SQLite 和 MySQL 都是非常流行的关系型数据库管理系统,但它们之间存在一些重要的差异:
1. 架构差异:MySQL 是一种典型的客户-服务器架构,由服务器进程来管理和处理所有数据库请求;而 SQLite 则没有独立的服务器进程,所有的操作都在客户端完成,可以直接与应用程序交互。
2. 大小和性能差异:MySQL 提供了许多高级特性,例如表分区、索引优化等,而且能够处理大规模的数据集;而 SQLite 则更加简单和紧凑,更适合小规模的应用程序和嵌入式环境。
3. 安全性和可靠性:MySQL 支持复杂的权限控制和安全机制;而 SQLite 主要依赖于操作系统和文件系统的安全措施。
总体来说,MySQL 更适合高性能、高并发的大规模应用,而 SQLite 则更适合资源受限的小型应用或嵌入式系统。
SQLite数据库安装:
1、执行 sqlite 指令
找不到相关内容说明没有安装过
2、执行安装指令
一般sqlite3的使用场景更多,运行“sudo apt-get -y install sqlite3”
sudo apt-get -y install sqlite3
运行“sudo apt-get -y install sqlite” ,此时的版本是2.8.17,大家直接装sqlite3
运行上面指令时报错E:dpkg was interrupted, you must manually run 'sudo dpkg --configure -a' to correct the problem.
“dpkg 已经被打断,您必须手动运行 'sudo dpkg --configure -a' 来纠正这个问题。” 这通常意味着你的 Ubuntu 系统正在尝试安装软件包的过程中遇到了某种错误,导致 dpkg (Debian 包管理器)无法正常工作。
按提示输入'sudo dpkg --configure -a'
再次输入安装指令
3、检查是否安装成功
完成后输入 sqlite3 查看
输入 .quit 可退出
SQLite数据库基本操作:
在工作目录“/home/orangepi/”下创建一个文件夹,将数据库文件存放在这里:
1、数据库的创建/打开
法1:
在刚刚创建的文件夹里面
- 输入“sqlite3” 进入数据库
- 输入“.open test.db”
- 输入“.quit” 退出
执行“.open A.db”时,如果一个数据库A.db不存在则会创建并打开;若该数据库已经存在就会打开;且 “.open A.db”不是SQL命令,对应的C接口是sqlite3_open( )函数
法2:
在刚刚创建的文件夹里面
- 输入“sqlite3 test.db” 在命令运行当前窗口创建数据库test.db
- 输入“.databases” 列出当前打开的数据库
- 输入“.quit” 退出
2、表格的创建/打开
数据库表格的创建和结构体很类似,在实际中也经常将两者进行转换:
create table sf(age Integer,name char,height Integer); //在这里“int”应该写成“Integer”,且写在变量名的后方
打开刚刚创建的test.db数据库,并在其中添加一张名为sf的表格,表格有三个字段:整型的age和height,char型的name
执行“create table AA(....)”时,如果表AA不存在则会创建并打开;若表格已经存在则会报错;且 “create table AA(....)”是SQL命令,对应的C接口是sqlite3_exec( )函数
3、插入数据
insert into sf values(18,'cxk',180);
//也可以此一次只插入部分的字段内容,比如只插入name和score,不插入id
insert into sf(name,height) values('jg',190); //插入部分字段内容
4、查看数据库的记录
select * from sf; //查询所有字段的结果
select name,score from sf; //查询数据库中部分字段的内容
5、删除一条记录
delete from sf where name = cxk;
6、更改一条记录
update stu set name = 'majia' where score = 77;
7、增加一列
alter table stu add column sex char;
8、删除一张表
drop table stu;
9、输入错误指令处理方式
当输入了无法识别的指令,数据库会进入“...>”的状态:
输入“CTRL + Z” 就可以强制退出:
SQLite的编程操作
在学习了SQlite的基本操作之后,虽然有能力进行数据基本的增删改查,但是都需要输入“sqlite3”在数据库命令行来执行,我们希望实现的是更自动化的运行,比如程序在获取数据后可以自动写入数据库,这就需要学习如何打开/创建数据库的C接口:
打开/创建数据库的C接口
sqlite3_open(const char *filename, sqlite3 **ppDb)
该指令打开一个指向 SQLite 数据库文件的连接,返回一个用于其他 SQLite 程序的数据库连接对象。
sqlite3_close(sqlite3*)
该指令关闭之前调用 sqlite3_open() 打开的数据库连接。
所有与连接相关的语句都应在连接关闭之前完成。 如果还有查询没有完成,sqlite3_close() 将返回 SQLITE_BUSY 禁止关闭的错误消息。
const char *sqlite3_errmsg(sqlite3*);
该指令通常用来获取最近调用的API接口返回的错误代码
错误代码:
test1.c
#include <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
sqlite3 *db;
char *zErrMsg = 0;
int ret;
if(argc < 2){
printf("Usage: %s xxx.db\n",argv[0]);
return -1;
}
ret = sqlite3_open(argv[1], &db);
if(ret != SQLITE_OK){
printf("Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}else{
printf("Opened database successfully\n");
}
sqlite3_close(db);
printf("close database\n");
}
编译运行,gcc编译需要链接sqlite3的库,执行a.out时给程序指定数据库test.db
如果test.db本来就存在,那么“./a.out test.db” 也会成功运行,只不过从“创建test.db并关闭”变成了“打开test.db并关闭”
sqlite3_exec函数的引入 & SELECT操作
`sqlite3_exec()` 是 SQLite 的一个内置函数,用于在指定的数据库连接上执行一个或多个 SQL 语句,并返回结果。它可以用于执行任何合法的 SQL 语句,包括 SELECT、INSERT、UPDATE 和 DELETE 等等。
使用 `sqlite3_exec()` 可以简化 SQLite 应用程序中的代码编写和维护工作,因为开发者只需要调用这个函数来执行 SQL 语句,而无需手动解析和处理查询结果。
此外,由于 `sqlite3_exec()` 支持回调函数,因此它还可以用于处理复杂的查询结果集。例如,当执行一个 SELECT 语句时,可以通过提供一个回调函数来处理每一行数据,以便实现更加灵活的数据处理方式。
sqlite3_exec(sqlite3*, const char *sql, sqlite_callback, void *data, char **errmsg)
其中:
- `db`: 是一个已打开的数据库连接。
- ` strSql`: 是要执行的 SQL 语句或多个 SQL 语句。
- `callback`: 是一个回调函数,用于处理查询结果。如果不需要处理查询结果,则可以将其设置为 `NULL`。
- `void*`: 是传递给回调函数的第一个参数。
- `errmsg`: 是一个指针,用于接收错误消息。
该函数返回 SQLITE_OK(表示成功)、SQLITE_ERROR、SQLITE_INTERNAL、SQLITE_PERM、SQLITE_ABORT 等等,具体取决于操作的结果。
int callback(void *arg, int column_size, char *column_value[], char *column_name[])
用于处理 SQLite 查询结果的回调函数。当使用 sqlite3_exec()
执行包含 SELECT 语句或其他需要获取查询结果的操作时,可以将此回调函数作为参数传递给该函数。
- 第一个参数void *arg 是sqlite3_exec函数的第四个参数data
- column_size:数据库的字段 数
- column_value[ ]:列的值
- column_name:字段名字
实现查看数据库记录指令 sqlite3_exec(db, "select * from sf;", callback, 0, &zErrMsg);
#include <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
int callback(void *arg, int column_size, char *column_value[], char *column_name[])
{
int i;
printf("arg=%s\n",(char *)arg);
for(i=0;i<column_size;i++){
printf("%s = %s\n", column_name[i], column_value[i]);
}
printf("=======================\n");
return 0;//必须返回0,这样数据库中有多少条数据,这个回调函数就会被调用多少次
}
int main(int argc, char** argv)
{
sqlite3 *db;
char *zErrMsg = 0;
int ret;
if(argc < 2){
printf("Usage: %s xxx.db\n",argv[0]);
return -1;
}
/*open database*/
ret = sqlite3_open(argv[1], &db);
if(ret != SQLITE_OK){
printf("Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}else{
printf("Opened database successfully\n");
}
/* execute sql statement */
ret = sqlite3_exec(db, "select * from sf;", callback, 0, &zErrMsg);
/* close database */
sqlite3_close(db);
printf("close database\n");
return 0;
}
编译运行,gcc编译需要链接sqlite3的库,执行a.out时给程序指定查看的数据库test.db
创建表 & 插入数据
#include <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
int callback(void *arg, int column_size, char *column_value[], char *column_name[])
{
int i;
printf("arg=%s\n",(char *)arg);
for(i=0;i<column_size;i++){
printf("%s = %s\n", column_name[i], column_value[i]);
}
printf("=======================\n");
return 0;//必须返回0,这样数据库中有多少条数据,这个回调函数就会被调用多少次
}
int main(int argc, char** argv)
{
sqlite3 *db;
char *zErrMsg = 0;
int ret;
char *sql;
if(argc < 2){
printf("Usage: %s xxx.db\n",argv[0]);
return -1;
}
/*open database*/
ret = sqlite3_open(argv[1], &db);
if(ret != SQLITE_OK){
printf("Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}else{
printf("Opened database successfully\n");
}
/* execute sql statement -- create table */
sql = "CREATE TABLE COMPANY(" \
"ID INT PRIMARY KEY NOT NULL," \
"NAME CHAR(30) NOT NULL," \
"AGE INT NOT NULL," \
"ADDRESS CHAR(50)," \
"SALARY REAL );";
ret = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
if(ret != SQLITE_OK){
printf("Can't create table: %s\n", sqlite3_errmsg(db));
exit(0);
}else{
printf("Table create successfully\n");
}
/* execute sql statement -- insert data */
sql = "insert into company values(1813,'company.1',3,'aaa street No.293',37732);";
ret = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
if(ret != SQLITE_OK){
printf("Can't insert data: %s\n", sqlite3_errmsg(db));
exit(0);
}else{
printf("Inset data successfully\n");
}
/* execute sql statement -- show content */
ret = sqlite3_exec(db, "select * from company;", callback, 0, &zErrMsg);
if(ret != SQLITE_OK){
printf("Can't show content: %s\n", sqlite3_errmsg(db));
exit(0);
}
/* close database */
sqlite3_close(db);
printf("close database\n");
return 0;
}
UPDATE操作 & DELETE操作
对于上一步创建的company表格,更新它的地址,然后删除这条记录
#include <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
int callback(void *arg, int column_size, char *column_value[], char *column_name[])
{
int i;
printf("arg=%s\n",(char *)arg);
for(i=0;i<column_size;i++){
printf("%s = %s\n", column_name[i], column_value[i]);
}
printf("=======================\n");
return 0;//必须返回0,这样数据库中有多少条数据,这个回调函数就会被调用多少次
}
int main(int argc, char** argv)
{
sqlite3 *db;
char *zErrMsg = 0;
int ret;
char *sql;
if(argc < 2){
printf("Usage: %s xxx.db\n",argv[0]);
return -1;
}
/*open database*/
ret = sqlite3_open(argv[1], &db);
if(ret != SQLITE_OK){
printf("Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}else{
printf("Opened database successfully\n");
}
/* execute sql statement -- create table */
sql = "CREATE TABLE COMPANY(" \
"ID INT PRIMARY KEY NOT NULL," \
"NAME CHAR(30) NOT NULL," \
"AGE INT NOT NULL," \
"ADDRESS CHAR(50)," \
"SALARY REAL );";
ret = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
if(ret != SQLITE_OK){
printf("Can't create table: %s\n", sqlite3_errmsg(db));
//exit(0); 这里将exit注释掉的原因是如果表格已经存在,不需要退出,而是继续执行即可
}else{
printf("Table create successfully\n");
}
/* execute sql statement -- show content */
ret = sqlite3_exec(db, "select * from company;", callback, "before update", &zErrMsg);
if(ret != SQLITE_OK){
printf("Can't show content1: %s\n", sqlite3_errmsg(db));
exit(0);
}
/* execute sql statement -- update data */
sql = "update company set address = 'ccc street No.666' where id = 1813;";
ret = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
if(ret != SQLITE_OK){
printf("Can't update data: %s\n", sqlite3_errmsg(db));
exit(0);
}else{
printf("Update data successfully\n");
}
/* execute sql statement -- show content */
ret = sqlite3_exec(db, "select * from company;", callback, "after update", &zErrMsg);
if(ret != SQLITE_OK){
printf("Can't show content2: %s\n", sqlite3_errmsg(db));
exit(0);
}
/* execute sql statement -- delete data */
sql = "delete from company where id = 1813;";
ret = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
if(ret != SQLITE_OK){
printf("Can't delete data: %s\n", sqlite3_errmsg(db));
exit(0);
}else{
printf("Delete data successfully\n");
}
/* execute sql statement -- show content */
ret = sqlite3_exec(db, "select * from company;", callback, "after delete", &zErrMsg);
if(ret != SQLITE_OK){
printf("Can't show content3: %s\n", sqlite3_errmsg(db));
exit(0);
}
/* close database */
sqlite3_close(db);
printf("close database\n");
return 0;
}
由于表格已经存在,所以创建表格的命令出错了,不过由于我的修改,代码会继续运行而不是直接exit;更新数据后,地址的确改变了;而删除数据后,由于本来就只有一条记录,所以此时company表没有数据,所以此时哪怕调用了“select * from company;” sqlite3_exec函数也不会调用callback来打印信息!
!!回归正题!!
代码实现:
项目源码:
https://pan.baidu.com/s/1KDznkE5dkpjBGxTbj9HnzQ
提取码:sail
1、使用数据库需包含头文件
2、定义变量
3、数据库和表创建函数
void create_db(){//创建/打开数据库并创建一个名为history的表格
int ret;
char *create_table_sql;
//打开数据库
ret = sqlite3_open("history.db", &db);
if(ret != SQLITE_OK){
printf("Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}else{
printf("Open database successfully\n");
}
//创建表格
//create_table_sql = "create table history(cause char,count Integer);";
create_table_sql = "CREATE TABLE HISTORY(" \
"CAUSE CHAR(30) PRIMARY KEY NOT NULL," \
"COUNT INT NOT NULL );" ;
ret = sqlite3_exec(db, create_table_sql, callback, 0, &zErrMsg);
if(ret != SQLITE_OK){
printf("Can't create table: %s\n", sqlite3_errmsg(db));
//exit(0);
}else{
printf("Table create successfully\n");
}
}
4、表初始化函数(插入初始化数据)
void init_table() //用于初始化数据库中表格的值
{
char *init_table_sql;
int ret;
init_table_sql = "insert into history values('socket',0);";
ret = sqlite3_exec(db, init_table_sql, callback, 0, &zErrMsg);
if(ret != SQLITE_OK){
printf("Can't init table value: %s\n", sqlite3_errmsg(db));
}
init_table_sql = "insert into history values('sound',0);";
ret = sqlite3_exec(db, init_table_sql, callback, 0, &zErrMsg);
if(ret != SQLITE_OK){
printf("Can't init table value: %s\n", sqlite3_errmsg(db));
}
}
5、写入数据
在上一节将数据存储到文件中我们创建了一个线程record,这一节我们将指令来源和次数保存到数据库中(SQLite)、指令来源和发出时间以及垃圾类型保存在文件中这两个步骤都放到record里面运行
分别写两个函数功能是将数据写入文件和数据库
void data_file() //将历史记录写入文件的函数
{
int hist_fd; // file description
int ret;
hist_fd = open("./history.txt",O_RDWR|O_CREAT|O_APPEND, 0666); //可读可写可打开的打开历史记录的文件,不存在就创建,且每次都追加写入
if(hist_fd < 0){
printf("fail to open history file!\n");
fflush(stdout);
}
ret = write(hist_fd, &hist_whole, strlen(hist_whole));
if(ret == -1){
printf("fail to write history write to file!\n");
fflush(stdout);
}else{
printf("write history to file successfully!\n");
//文件内数据和数据库里面的数据一起显示有点乱我就先注释掉了
/*printf("write the following history to file:\n");
printf("------------------------\n");
printf("%s",hist_whole);
printf("------------------------\n");*/
fflush(stdout);
}
close(hist_fd);
memset(hist_whole,'\0',sizeof(hist_whole)); //清空hist_whole!
}
void data_sql() //将历史记录写入数据库的函数
{
char update_sql[128] = {'\0'};
int ret;
// printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
sprintf(update_sql,"update history set count = %d where cause = 'socket';",sock_cmd);//客户端指令
//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
ret = sqlite3_exec(db, (const char *)update_sql, callback, 0, &zErrMsg);
if(ret != SQLITE_OK){
printf("Can't update date: %s\n", sqlite3_errmsg(db));
}
sprintf(update_sql,"update history set count = %d where cause = 'sound';",sond_cmd);//语音指令
ret = sqlite3_exec(db, (const char *)update_sql, callback, 0, &zErrMsg);
if(ret != SQLITE_OK){
printf("Can't update date: %s\n", sqlite3_errmsg(db));
}
ret = sqlite3_exec(db, "select * from history;", callback, 0, &zErrMsg); //将数据库数据打印到屏幕
if(ret != SQLITE_OK){
printf("Can't show date: %s\n", sqlite3_errmsg(db));
}
//清零
int sock_cmd = 0;//客户端指令
int sond_cmd = 0;//语音指令
}
将这两个函数在线程record里面调用
6、发生数据记录
我们统计指令来源(客户端、语音模块)在对应触发语句将各自变量+1
项目效果: