赞
踩
目录
对于主键和外键知识点的补充:
在 C/C++ 程序中使用 SQLite 之前,我们需要确保机器上已经有 SQLite 库,这个库提供了C/C++的操作SQLite的编程接口API。
https://www.cnblogs.com/tfanalysis/p/4073756.html
我使用的是Windows下通过vscode远程SSH访问Linux,因此需要在windows下也安装并添加相应的库文件。
安装前,我们在vscode中添加SQLite的头文件,提示找不到头文件
因此我们需要先定位头文件包含路径
然后到sqlite官网下载源码包(sqlite3的源码)
https://www.cnblogs.com/White-strategy-group/p/6360003.html
解压缩后文件内容如图所示
将三个.h文件添加到之前的includePath中:"D:/myinclude/**"
我们先在目录下新建一个sqlite文件夹
然后将头文件添加进来
如果发现添加完之后仍然自动找不到头文件,如下所示
我们需要手动包含具体头文件路径,如:"D:\\myinclude\\sqlite3"
我们输入sqlite3发现,可以自动提示补全,则表示库文件添加成功!
第一个参数为指定要打开的数据库的名字,也包括数据库的路径
第二个参数为一个二级指针,这里的作用相当于文件描述符,它是一个数据库文件指针。传入的是一个一级指针的地址作为输出,给指针的具体指向赋值(定义一个空指针传入过来,最终会给这个指针赋值)(通过操作数据库文件指针就相对于操作数据库)
SQLite3的C/C++接口函数所有返回值如下:
- #define SQLITE_OK 0 /* Successful result */
- #define SQLITE_ERROR 1 /* SQL error or missing database */
- #define SQLITE_INTERNAL 2 /* An 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 /* A malloc() failed */
- #define SQLITE_READONLY 8 /* Attempt to write a readonly database */
- #define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite_interrupt() */
- #define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */
- #define SQLITE_CORRUPT 11 /* The database disk image is malformed */
- #define SQLITE_NOTFOUND 12 /* (Internal Only) 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 /* Database lock protocol error */
- #define SQLITE_EMPTY 16 /* (Internal Only) Database table is empty */
- #define SQLITE_SCHEMA 17 /* The database schema changed */
- #define SQLITE_TOOBIG 18 /* Too much data for one row of a table */
- #define SQLITE_CONSTRAINT 19 /* Abort due to contraint 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_ROW 100 /* sqlite_step() has another row ready */
- #define SQLITE_DONE 101 /* sqlite_step() has finished executing */
返回错误信息
返回值错误码
- #include <stdio.h>
- #include <sqlite3.h>
-
- int main(int argc, char const *argv[])
- {
- sqlite3 *db;
- int ret = sqlite3_open(argv[1],&db);
-
- if(ret != SQLITE_OK)
- {
- printf("sqlite3 open:%s\n",sqlite3_errmsg(db));
- exit(-1);
- }
-
- printf("sqlite open db successfully!\n");
-
- sqlite3_close(db);
- return 0;
- }
编译的时候,不能直接编译
我们需要像使用POSIX库一样,手动链接sqlite3库
运行结果:
但是该函数有个bug,即使不传任何参数,也不会报错
因此最好添加一个命令行传参判断
- #include <stdio.h>
- #include <sqlite3.h>
-
- int main(int argc, char const *argv[])
- {
- if(argc != 2)
- {
- printf("Please input db name!\n");
- exit(-1);
- }
-
- sqlite3 *db;
- int ret = sqlite3_open(argv[1],&db);
-
- if(ret != SQLITE_OK)
- {
- printf("sqlite3 open:%s\n",sqlite3_errmsg(db));
- exit(-1);
- }
-
- printf("sqlite open db successfully!\n");
-
- sqlite3_close(db);
- return 0;
- }
执行SQL语句函数,该函数一共有5个参数
参数1:数据库文件句柄
参数2:要执行的SQL语句
参数3:回调函数,传入一个函数指针(这里的sqlite_callback callback中的sqlite_callback是通过函数指针重命名的,如下图所示)。注:回调函数只对SQL查询语句有效。当指定的是一个非查询操作,该参数应该置为NULL,否则即使传入了回调函数,回调函数也不会被执行。
参数4:回调函数的参数
参数5:保存执行SQL后的错误信息,传入的是一级指针的地址
我们首先创建一个学生表,其中我们对错误检查进行了二次封装
- #include <stdio.h>
- #include <sqlite3.h>
- #include <string.h>
- #include <stdlib.h>
-
- void print_error(int ret, char *err, sqlite3 *db)
- {
- if(ret != SQLITE_OK)
- {
- printf("%s:%s\n",err,sqlite3_errmsg(db));
- exit(-1);
- }
- }
-
- int main(int argc, char const *argv[])
- {
- if(argc != 2)
- {
- printf("Please input db name!\n");
- exit(-1);
- }
-
- sqlite3 *db;
- char *errmsg;
- char sql[1024] = {0};
- int ret = sqlite3_open(argv[1],&db);
-
- print_error(ret,"sqlite open",db);
-
- printf("sqlite open db successfully!\n");
-
- strcpy(sql,"create table student(id integer primary key, name text, age integer)");
-
- ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);
-
- print_error(ret,"sqlite exec create table",db);
-
- printf("create table successfully\n");
-
- sqlite3_close(db);
- return 0;
- }
然后通过键盘输入的方式往表里插入三条数据
- #include <stdio.h>
- #include <sqlite3.h>
- #include <string.h>
- #include <stdlib.h>
-
- void print_error(int ret, char *err, sqlite3 *db)
- {
- if(ret != SQLITE_OK)
- {
- printf("%s:%s\n",err,sqlite3_errmsg(db));
- exit(-1);
- }
- }
-
- int main(int argc, char const *argv[])
- {
- if(argc != 2)
- {
- printf("Please input db name!\n");
- exit(-1);
- }
-
- sqlite3 *db;
- char *errmsg;
- char sql[1024] = {0};
-
- int id;
- char name[20];
- int age;
-
- int ret = sqlite3_open(argv[1],&db);
-
- print_error(ret,"sqlite open",db);
-
- printf("sqlite open db successfully!\n");
-
- strcpy(sql,"create table if not exists student(id integer primary key, name text, age integer)");
-
- ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);
-
- print_error(ret,"sqlite exec create table",db);
-
- printf("create table successfully\n");
-
- //插入3行数据:id,name,age 键盘输入
- for(int i=0;i<3;i++)
- {
- printf("Please input id:\n");
- scanf("%d",&id);
-
- printf("Please input name:\n");
- scanf("%s",name);
-
- printf("Please input age:\n");
- scanf("%d",&age);
-
- //sql:insert into student(id,name,age)values();
- //sprint();写入到字符串 fprintf();写入到文件
- memset(sql,0,sizeof(sql));
- sprintf(sql,"insert into student(id,name,age)values(%d,'%s',%d)",id,name,age);
- ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);
- print_error(ret,"sqlite exec create table",db);
- }
-
- sqlite3_close(db);
- return 0;
- }
如果我们想要删除zhangsan的数据,可以使用如下
- #include <stdio.h>
- #include <sqlite3.h>
- #include <string.h>
- #include <stdlib.h>
- #define DELETE_DATA 1
- #define INSERT_DATA 0
- void print_error(int ret, char *err, sqlite3 *db)
- {
- if(ret != SQLITE_OK)
- {
- printf("%s:%s\n",err,sqlite3_errmsg(db));
- exit(-1);
- }
- }
-
- int main(int argc, char const *argv[])
- {
- if(argc != 2)
- {
- printf("Please input db name!\n");
- exit(-1);
- }
-
- sqlite3 *db;
- char *errmsg;
- char sql[1024] = {0};
-
- int id;
- char name[20];
- int age;
-
- int ret = sqlite3_open(argv[1],&db);
-
- print_error(ret,"sqlite open",db);
-
- printf("sqlite open db successfully!\n");
-
- strcpy(sql,"create table if not exists student(id integer primary key, name text, age integer)");
-
- ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);
-
- print_error(ret,"sqlite exec create table",db);
-
- printf("create table successfully\n");
-
- #if INSERT_DATA
- //插入3行数据:id,name,age 键盘输入
- for(int i=0;i<3;i++)
- {
- printf("Please input id:\n");
- scanf("%d",&id);
-
- printf("Please input name:\n");
- scanf("%s",name);
-
- printf("Please input age:\n");
- scanf("%d",&age);
-
- //sql:insert into student(id,name,age)values();
- //sprint();写入到字符串 fprintf();写入到文件
- memset(sql,0,sizeof(sql));
- sprintf(sql,"insert into student(id,name,age)values(%d,'%s',%d)",id,name,age);
- ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);
- print_error(ret,"sqlite exec create table",db);
- }
- #endif
-
- #if DELETE_DATA
- printf("Please input who do you want to delete:\n");
- memset(sql,0,sizeof(sql));
- memset(name,0,sizeof(name));
- scanf("%s",name);
- sprintf(sql,"delete from student where name = '%s'",name);
- ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);
- print_error(ret,"sqlite exec delete data",db);
- #endif
- sqlite3_close(db);
- return 0;
- }
注:SQLite库提供的是原生态的接口,我们可以对其进行二次封装,像之前错误检查那样,对sqlite3_exec要执行的SQL语句,进行封装,避免代码的冗余。
每查询到一条结果就会回调一次这个函数,通过一行行缓冲数据,每次缓冲一行。
参数1:传入的参数
参数2:保存查询到的结果每一行中列的个数
参数3:保存查询到数据中每一列的值,用一个指针数组(保存指针的数组,本质是数组)来接
参数4:保存每一列的字段名字,用一个指针数组来接
以打印查询到的结果每行列数为例:如果回调函数不加return 0;,那么将只执行一次
将上return 0;,才可以执行全部
如果想要打印查询到的每一列结果
再加上每一列相应的字段名
注意:对于外部传入回调函数的参数是无法修改的(具体原因可能是由于内部机制)
如:我们传入一个flag变量,然后出函数打印结果
我们在回调函数内对flag进行++
但出函数之后,值仍是0
如果查询不到结果,将会什么信息也不会输出
我们可以通过定义一个全局变量标志位进行判断,是否查询到数据
- #include <stdio.h>
- #include <sqlite3.h>
- #include <stdlib.h>
- #include <string.h>
-
- int flag=0;
-
- void print_error(int ret, char *err, sqlite3 *db)
- {
- if(ret != SQLITE_OK)
- {
- printf("%s:%s\n",err,sqlite3_errmsg(db));
- exit(-1);
- }
- printf("%s:successfully!\n",err);
- }
-
- int my_sqlite_callback(void *para,int columnCount,char **columnValue,char**columnName)
- {
- printf("columnCount = %d\n", columnCount);
- flag=1;
- for(int i = 0; i < columnCount;i++)
- {
- printf("%s:%s|",columnName[i],columnValue[i]);
- }
- printf("\n");
- return 0;
- }
-
- int main(int argc, char const *argv[])
- {
- if(argc != 2)
- {
- printf("Please input db name!\n");
- exit(-1);
- }
-
- sqlite3 *db;
- char *errmsg;
- char sql[1024] = {0};
-
- int ret = sqlite3_open(argv[1],&db);
-
- print_error(ret,"sqlite open",db);
-
- printf("sqlite open db successfully!\n");
-
-
- strcpy(sql,"select * from student where name = 'zhangsan'");
- sqlite3_exec(db,sql,my_sqlite_callback,NULL,&errmsg);
- print_error(ret,"select",db);
- if(flag==0)
- {
- printf("The data queried is empty!\n");
- }
- sqlite3_close(db);
- return 0;
- }
与sqlite_exec不同,sqlite3_get_table是专门用于查询数据的,通过一次性将所有查询到的数据缓冲起来
参数1:数据库文件句柄
参数2:数据库SQL语句
参数3:三维指针,用于保存查询到的结果
参数4:查询到的结果总共的行数
参数5:查询到的结果总共的列数
参数6:保存查询出错的信息
以三维指针为例,我们可以创建一个变相的二维数组:
可以想象创建一个长方体
char ***result;
result = (char***)malloc(sizeof(char**)*4);创建四个存储空间
*result = (char**)malloc(sizeof(char*)*4);每个存储空间里再创建四个存储空间
**result = (char*)malloc(sizeof(char)*4);每个存储空间里再创建一个字符串数组
最终在逻辑上形成16个连续的存储空间(物理上不连续)
我们可以通过三次for循环来创建
但访问方式仍是一维数组的访问方式,因为通过指针创建的空间,本质还是链式的,不是真正的多维数组
实际使用时,我们需要定义一个二维指针,将它的地址作为参数传入,查询到的数据都将保存在二维指针中
发现打印是从字段开始打印的,最后少了一行数据,这是因为保存的数据包括了字段那一行,但是返回的行数nrow只算了实际数据的行数
因此需要改正如下:
这样输出的结果就是正确的了
最后一定要记得调用释放空间函数sqlite3_free_table,因为库函数sqlite3_get_table的内部分配了堆区空间
全缓冲查询程序如下:
- #include <stdio.h>
- #include <sqlite3.h>
- #include <stdlib.h>
- #include <string.h>
-
- void print_error(int ret, char *err, sqlite3 *db)
- {
- if(ret != SQLITE_OK)
- {
- printf("%s:%s\n",err,sqlite3_errmsg(db));
- exit(-1);
- }
- printf("%s:successfully!\n",err);
- }
-
-
- int main(int argc, char const *argv[])
- {
- if(argc != 2)
- {
- printf("Please input db name!\n");
- exit(-1);
- }
-
- sqlite3 *db;
- char *errmsg;
- char sql[1024] = {0};
-
- int ret = sqlite3_open(argv[1],&db);
-
- print_error(ret,"sqlite open",db);
-
- printf("sqlite open db successfully!\n");
-
- char **result;
- int nrow;
- int ncolumn;
- strcpy(sql,"select * from student");
- ret = sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg);
- print_error(ret,"select",db);
-
- for(int i = 1; i<=nrow; i++)
- {
- for(int j = 0; j < ncolumn; j++)
- {
- printf("%s|",result[i*ncolumn+j]);
- }
- printf("\n");
- }
-
- sqlite3_free_table(result);
- sqlite3_close(db);
- return 0;
- }
sqlite3_prepare
作用:把SQL语句转成字节码,由后面的执行函数去执行,将查询到的数据做字节缓冲
参数1:数据库文件句柄
参数2:SQL语句
参数3:SQL语句的最大字节数,一般设为-1
参数4:Statement句柄,即字节序句柄
参数5:SQL语句无用部分的指针,一般设为NULL
字节缓冲查询还涉及到了以下函数:
- #include <stdio.h>
- #include <sqlite3.h>
- #include <stdlib.h>
- #include <string.h>
-
- void print_error(int ret, char *err, sqlite3 *db)
- {
- if(ret != SQLITE_OK)
- {
- printf("%s:%s\n",err,sqlite3_errmsg(db));
- exit(-1);
- }
- printf("%s:successfully!\n",err);
- }
-
-
- int main(int argc, char const *argv[])
- {
- if(argc != 2)
- {
- printf("Please input db name!\n");
- exit(-1);
- }
-
- sqlite3 *db;
- char *errmsg;
- char sql[1024] = {0};
-
- int ret = sqlite3_open(argv[1],&db);
-
- print_error(ret,"sqlite open",db);
-
- printf("sqlite open db successfully!\n");
-
-
- int rc,i,j;
- int ncolumn;
- sqlite3_stmt *stmt;
- strcpy(sql,"select * from student");
-
- rc = sqlite3_prepare(db,sql,-1,&stmt,NULL);
- if(rc)
- {
- printf("query fail!\n");
- }
- else
- {
- printf("query success!\n");
- rc = sqlite3_step(stmt);//查询成功,则返回值rc==SQLITE_ROW
- ncolumn = sqlite3_column_count(stmt);//获取列数
- while(rc == SQLITE_ROW)
- {
- for(i = 0;i<ncolumn;i++)
- {
- printf("%s|",sqlite3_column_text(stmt,i));//获取每一列的数据
- }
- printf("\n");
- rc = sqlite3_step(stmt);//继续获取下一行数据
- }
- }
- sqlite3_finalize(stmt);
- sqlite3_close(db);
- return 0;
- }
回调函数查询内存开销小,但查询效率相对较低;全缓冲查询的查询效率高,但是内存消耗大;字节缓冲查询兼具查询效率和低开销。(优先使用第三种查询方法)
由于数据库提供的API接口过于复杂,使用的过程顺序也很繁琐,所以对于原生态的API在实际工作开发中,会进行一层封装,减少调用传参,减少调用次数,增加代码可读性,提高开发效率。
可封装如下:包括创建数据库、建表、插入数据、查询数据、删除数据
database.h
- #ifndef _DATABASE_H_
- #define _DATABASE_H_
-
- #include <stdio.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <libgen.h>
- #include <sqlite3.h>
-
- extern int open_database(sqlite3 **db,char *database_name);
- extern int create_table(sqlite3 **db,char *table_name,char *table_attribute);
- extern int insert_data(sqlite3 **db,char *table_name,char *attr,char *msg);
- extern int query_data(sqlite3 **db,char ***azResult,char *table_name);
- extern int delete_data(sqlite3 **db,char *table_name);
-
- #endif
database.c
- #include "database.h"
-
- char sql[128];
- char *zErrMsg=NULL;
- int nrow=0;
- int ncolumn = 0;
-
-
- int open_database(sqlite3 **db,char *database_name)
- {
- int len;
-
- len = sqlite3_open(database_name,db);
- if(len)
- {
- printf("Open database name %s failure.\n",database_name);
- sqlite3_close(*db);
- return -1;
- }
- printf("Open a sqlite3 database name %s successfully!\n",database_name);
-
- return 0;
- }
-
- int create_table(sqlite3 **db,char *table_name,char *table_attribute)
- {
-
-
- snprintf(sql,sizeof(sql),"CREATE TABLE %s(%s);",table_name,table_attribute);
- //log_info("sql=%s\n",sql);
-
- //sql="CREATE TABLE test(TEST CHAR(100));";
-
- if(sqlite3_exec(*db,sql,NULL,NULL,&zErrMsg)!=SQLITE_OK)
- {
- printf("Table %s already exist\n",table_name);
- }
- else
- {
- printf("Create table %s successfully\n",table_name);
- }
-
- }
-
- int insert_data(sqlite3 **db,char *table_name,char *attr,char *msg)
- {
- snprintf(sql,sizeof(sql),"INSERT INTO %s(%s) VALUES('%s');",table_name,attr,msg); //插入数据
-
- if(sqlite3_exec(*db,sql,NULL,NULL,&zErrMsg)!=SQLITE_OK)
- {
- sqlite3_close(*db);
- printf("Insert %s to table %s failure:%s\n",msg,table_name,strerror(errno));
- return -1;
- }
- printf("Insert %s to table %s successfully\n",msg,table_name);
-
- return 0;
- }
-
-
- int query_data(sqlite3 **db,char ***azResult,char *table_name)
- {
- snprintf(sql,sizeof(sql),"select *from %s;",table_name);
- //sql="select *from test";
-
- if(sqlite3_get_table(*db,sql,azResult,&nrow,&ncolumn,&zErrMsg)!=SQLITE_OK)
- {
- sqlite3_close(*db);
- printf("Select *from %s failure\n",table_name);
- return -1;
- }
- printf("There are %d pieces of data in table %s\n",nrow,table_name);
-
- return nrow;
- }
-
- int delete_data(sqlite3 **db,char *table_name)
- {
- snprintf(sql,sizeof(sql),"delete from %s;",table_name);
- //sql="delete from test";
-
- if(sqlite3_exec(*db,sql,NULL,NULL,&zErrMsg)!=SQLITE_OK)
- {
- sqlite3_close(*db);
- printf("Delete from %s failure\n",table_name);
- return -1;
- }
- printf("Delete data from table %s successfully!\n",table_name);
-
- return 0;
- }
test_database.c
- #include "database.h"
-
-
- int main(void)
- {
- sqlite3 *db;
- char **azResult=NULL;
-
- if( open_database(&db,"test.db")<0 )
- return -1;
-
- if( create_table(&db,"test","TEST CHAR(100)")<0 )
- return -2;
-
- if( insert_data(&db,"test","TEST","Test nihao")<0 )
- return -3;
-
- if( query_data(&db,&azResult,"test")<0 )
- return -4;
-
- if( delete_data(&db,"test")<0 )
- return -5;
-
- if( query_data(&db,&azResult,"test")<0 )
- return -6;
-
- return 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。