Linux下基于SQLite3 实现商店商品管理系统
水果店账单管理系统
一.SQLite相关C接口
如果第一次直接在命令行安装sqlite3,是不包含sqlite3.h这个包的,我们需要单独下载sqlite3支持的库,即通过命令行:
sudo apt install libsqlite3-dev
如果我们不知道该安装什么包来提供sqlite3的C/C++接口,可以通过debian官网通过查询软件包关键词可以知道库是依赖libsqlite3-dev包的。
注意,链接的时候要加上-lsqlite3
表示依赖第三方库
关于创建一个数据库,可以通过create table stu (id integer, name text)
,但是如果连续创建的话会报错,因为已经存在了,所以我们可以使用create table if not exists stu (id integer, name text)
来防止重复创建报错。
可以通过句柄来获取错误消息
重要接口
以下是重要的 C&C++ / SQLite 接口程序,基本可以您在 C/C++ 程序中使用 SQLite 数据库的需求。
序号 | API & 描述 |
---|---|
1 | *sqlite3_open(const char *filename, sqlite3 *ppDb) 该例程打开一个指向 SQLite 数据库文件的连接,返回一个用于其他 SQLite 程序的数据库连接对象。如果 filename 参数是 NULL 或 ‘:memory:’,那么 sqlite3_open() 将会在 RAM 中创建一个内存数据库,这只会在 session 的有效时间内持续。如果文件名 filename 不为 NULL,那么 sqlite3_open() 将使用这个参数值尝试打开数据库文件。如果该名称的文件不存在,sqlite3_open() 将创建一个新的命名为该名称的数据库文件并打开。 |
2 | *sqlite3_exec(sqlite3*, const char *sql, sqlite_callback, void *data, char *errmsg) 该例程提供了一个执行 SQL 命令的快捷方式,SQL 命令由 sql 参数提供,可以由多个 SQL 命令组成。在这里,第一个参数 sqlite3 是打开的数据库对象,sqlite_callback 是一个回调,data 作为其第一个参数,errmsg 将被返回用来获取程序生成的任何错误。sqlite3_exec() 程序解析并执行由 sql 参数所给的每个命令,直到字符串结束或者遇到错误为止。 |
3 | sqlite3_close(sqlite3*) 该例程关闭之前调用 sqlite3_open() 打开的数据库连接。所有与连接相关的语句都应在连接关闭之前完成。如果还有查询没有完成,sqlite3_close() 将返回 SQLITE_BUSY 禁止关闭的错误消息。 |
sqlite3_exec函数原型如下:
int sqlite3_exec(
sqlite3*, /* 数据库句柄 */
const char *sql, /* 执行的SQLite命令 */
int (*callback)(void*,int,char**,char**), /* Callback function */
void *, /* 向回调函数传递的参数是 */
char **errmsg /* Error msg written here */
);
sqlite3_exec()
是一个通用函数,可以执行SQLite命令行,相当于间接使用命令行来操作数据库。
打开数据库
直接调用sqlite3_open()
打开一个数据库。
检测返回值是否为SQLITE_OK,如果不是,表示出错了,出错信息的获取通过句柄可以获取,如下:
const char *sqlite3_errmsg(sqlite3*);
SQLite有31种返回值,如下:
#define SQLITE_OK 0 /* Successful result */
/* beginning-of-error-codes */
#define SQLITE_ERROR 1 /* Generic error */
#define SQLITE_INTERNAL 2 /* 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 sqlite3_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 /* Unknown opcode in sqlite3_file_control() */
#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 use only */
#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 /* Not used */
#define SQLITE_RANGE 25 /* 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() has another row ready */
#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */
插入信息
基于sqlite3_exec()
使用命令行语句来插入信息,根据上面所讲,插入的命令行如下:
2-- 插入一条记录
insert into stuinfo values(1001, 'zhangshangngsan', 18, 80);
insert into stuinfo (id, name, score) values(1002, 'lisi', 90);
可以按照全部元素的顺序插入,也可以指定元素插入
插入多条的时候直接在后面加上即可
insert into stuinfo (id, name, score) values(1002, 'lisi', 90), (1001, 'zhangshan', 18, 80);
先读取到要插入的信息,然后使用sprintf函数格式化为字符串,最后将字符串传递给sqlite3_exec()
的第二个参数const char *sql
来执行命令行操作数据库。
查询
查询有两种基础方法
- 基于sqlite3_exec和回调函数,参考链接:https://sqlite.org/c3ref/exec.html
- 直接使用sqlite3_get_table函数,参考链接:https://sqlite.org/c3ref/free_table.html
sqlite3_exec函数原型如下:
int sqlite3_exec(
sqlite3*, /* 数据库句柄 */
const char *sql, /* 执行的SQLite命令 */
int (*callback)(void*,int,char**,char**), /* Callback function */
void *, /* 向回调函数传递的参数是 */
char **errmsg /* Error msg written here */
);
sqlite3_exec()
是一个通用函数,可以执行SQLite命令行,相当于间接使用命令行来操作数据库。
查询的结果通过回调函数反馈,有几行记录,就调用几次回调函数,所以每次回调函数的执行都是对一行信息的查询。
关于回调函数有如下说明:
- 有几条记录就会调用几次回调函数
- 回调函数的参数通过sqlite3_exec的第三个参数传递,给回调函数的第一个参数
- 每条记录中有几列,就会传给第二个参数column
- 第三个参数就是对应字段内容
char **value
,以字符串的形式传递 - 第四个参数char ** name就是字段名
- 二级指针的用法和main函数的参数
char *argv[]
一样 - 回调函数返回的应该是0 表示正确,返回1代表出错
回调函数的定义可以如下:
static int callback(void *data, int argc, char **argv, char **azColName){
int i;
for(i=0; i<argc; i++){
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
还有,可以通过sqlite3_get_table
来获取表信息,sqlite3_get_table 专门用于查询语句,不需要回调了:
int sqlite3_get_table(
sqlite3 *db, /* 句柄 */
const char *zSql, /* SQL to be evaluated */
char ***pazResult, /* 获取的表信息,传递参数时直接一个二维数组的地址 */
int *pnRow, /* 行数,包括数据名称 */
int *pnColumn, /* 列数 */
char **pzErrmsg /* 出错信息 */
);
直接通过行数和列数来输出信息,不过要注意的是,返回到的查询信息,开始是表的数据名称,后面才是每个行的内容。
二.程序要求
假如我家开了个水果超市,有以下水果,想实现自动化管理,扫描二维码就能知道当前的水果状态,进货几天了,
好久需要再次进货,那些水果畅销,那些水果不畅销,那些水果春夏秋冬的价格波动,好,那么现在我想将
这些信息保存在数据库中,那么我应该怎么做;
提示: 建立一张fruit表,
假如水果有: 苹果,香蕉,梨,橘子,葡萄…(可以自己查一下英文保存到数据库)
水果价格: 苹果 5元/斤 香蕉 3元/斤 梨 3.5元/斤 橘子2.5元/斤 葡萄 8元/斤…
当前存货: 苹果 80斤 香蕉 200斤 梨 50斤 橘子300斤 葡萄 100斤…
超市每天水果都有进货和卖出嘛,水果的价格随着季节和天气也会有波动,顾客也会看一下每天水果的价格的嘛,
所以要求,根据上述提示,利用数据库完成水果店各种水果的增(进货)删(卖出)改(波动)查(看价格)功能。
并将进出货的时间和顾客光顾的时间记录到数据库中保存。
三.程序说明
题目要求:
所以要求,根据上述提示,利用数据库完成水果店各种水果的增(进货)删(卖出)改(波动)查(看价格)功能。
并将进出货的时间和顾客光顾的时间记录到数据库中保存。
程序执行:
直接make即可 显示界面,按照指示输入指令即可实现上述所有功能
make clean用于清除二进制可执行文件
程序说明:
main函数中循环执行菜单栏目
根据用户在菜单栏目中的输入跳转到相应的功能区执行,每项功能都封装在一个函数中
功能区描述如下:
int menu(void); //菜单
|
----void select_fruit(void); //产看所有货物信息
void add_fruit(void); //增加新货物信息
void delete_fruit(void); //删除货物信息
void upadte_fruit(void); //更新信息
|
----void purchase_control(void); //进货
void sell_control(void); //售卖
void price_change(void); //价格变动
更新信息中可以进行:进货、售卖、价格变动等操作,也可退回上一菜单
进货时间在进货、增加货物信息的时候更新
顾客光顾时间在售卖的时候更新
四.实现代码
#include <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
static int callback(void*,int,char**,char**);
int menu(void); //菜单
void select_fruit(void); //产看所有货物信息
void add_fruit(void); //增加新货物信息
void delete_fruit(void); //删除货物信息
void upadte_fruit(void); //更新信息
void purchase_control(void); //进货
void sell_control(void); //售卖
void price_change(void); //价格变动
static char sql[128] = {0};
static sqlite3 *ppdb; //句柄
static int ret;
#define BUFLEN 255
char tmpBuf[BUFLEN]; //时间字符串
int main(int argc, const char *argv[])
{
int n;
//open the SQLite3
if (sqlite3_open("./fruit.db", &ppdb) != SQLITE_OK) {
printf("open filed:%s\n", sqlite3_errmsg(ppdb));
exit(1);
}
time_t t = time( 0 );
strftime(tmpBuf, BUFLEN, "%Y-%m-%d %H:%M", localtime(&t));
printf("%s\n\n\n", tmpBuf);
//创建数据库,在系统初始化的时候使用一次
// memset(sql, 0, sizeof(sql));
// sprintf(sql, "create table fruit (name, price, stockpile, purchase_time, sell_time);");
// ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
// if (ret != SQLITE_OK) {
// printf("create filed:%s\n", sqlite3_errmsg(ppdb));
// exit(1);
// }
while (1) {
n = menu();
switch (n)
{
case 1:
select_fruit();
break;
case 2:
add_fruit();
break;
case 3:
upadte_fruit(); //包括 进货、售出、价格修改
break;
case 4:
delete_fruit();
break;
case 5:
sqlite3_close(ppdb);
exit(0);
default:
printf("wrong number!!!\n");
break;
}
}
return 0;
}
int menu(void)
{
int n;
printf("---- Welcome to GQ's Fruit commodity management system! ----\n");
printf("- (please input the number as follows) -\n");
printf("- 1.select all commodity -\n");
printf("- 2.add commodity -\n");
printf("- 3.upadte commodity -\n");
printf("- 4.delete commodity -\n");
printf("- 5.exit system -\n");
printf("------------------------------------------------------------\n");
printf("input number here:");
scanf("%d", &n);
return n;
}
//查看所有货物信息
void select_fruit(void)
{
memset(sql, 0, sizeof(sql));
sprintf(sql, "select * from fruit;");
ret = sqlite3_exec(ppdb, sql, callback, NULL, NULL); //useing callback function
if (ret != SQLITE_OK) {
printf("select filed:%s\n", sqlite3_errmsg(ppdb));
exit(1);
}
printf("select ok!\n\n\n");
}
static int callback(void *data, int column_number, char *column_data[], char *column_name[])
{
int i;
for (i = 0; i < column_number; i++) {
if (i == column_number-1)
printf("%13s=%7s ", column_name[i], column_data[i]);
else
printf("%13s=%9s , ", column_name[i], column_data[i]);
}
puts("");
return 0;
}
//完成添加新货物信息
void add_fruit(void)
{
char name[10];
float price;
int stockpile;
printf("please input the fruit message:\n");
printf(" name price stockpile \n>");
scanf("%s", name);
scanf("%f", &price);
scanf("%d", &stockpile);
getchar(); //吃掉回车
time_t t = time( 0 );
strftime(tmpBuf, BUFLEN, "%Y-%m-%d %H:%M", localtime(&t));
memset(sql, 0, sizeof(sql));
sprintf(sql, "insert into fruit values('%s', %f, %d, '%s', NULL);", name, price, stockpile, tmpBuf);//注意冒号
ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
if (ret != SQLITE_OK) {
printf("add filed:%s\n", sqlite3_errmsg(ppdb));
exit(1);
}
printf("add success!\n\n\n");
}
//完成删除货物信息
void delete_fruit(void)
{
char name[10];
printf("input delete fruit:");
scanf("%s", name);
getchar();
memset(sql, 0, sizeof(sql));
sprintf(sql, "delete from fruit where name='%s';", name);//注意冒号
ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
if (ret != SQLITE_OK) {
printf("delete filed:%s\n", sqlite3_errmsg(ppdb));
exit(1);
}
printf("delete %s success\n\n\n", name);
}
//完成进货、卖出、价格波动
void upadte_fruit(void)
{
int n;
printf("---- Welcome to GQ's Fruit commodity management system! ----\n");
printf("- (please input the number as follows) -\n");
printf("- 1.purchase control -\n");
printf("- 2.sell control -\n");
printf("- 3.price change -\n");
printf("- 4.last step -\n");
printf("- 5.exit system -\n");
printf("------------------------------------------------------------\n");
printf("input number here:");
scanf("%d", &n);
switch (n)
{
case 1:
purchase_control();
break;
case 2:
sell_control();
break;
case 3:
price_change();
break;
case 4:
return ;
break;
case 5:
sqlite3_close(ppdb);
break;
default:
break;
}
}
void purchase_control(void)
{
char name[10];
int stockpile;
printf("input fruit name:");
scanf("%s", name);
getchar();
printf("input purchase quantity:");
scanf("%d", &stockpile);
getchar();
sprintf(sql, "update fruit set stockpile=%d where name='%s';", stockpile, name);
ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
if (ret != SQLITE_OK) {
printf("purchase_control filed:%s\n", sqlite3_errmsg(ppdb));
exit(1);
}
printf("purchase_control %s success\n\n\n", name);
}
void sell_control(void)
{
char name[10];
int stockpile;
printf("input fruit name:");
scanf("%s", name);
getchar();
printf("input new stockpile:");
scanf("%d", &stockpile);
getchar();
time_t t = time( 0 );
strftime(tmpBuf, BUFLEN, "%Y-%m-%d %H:%M", localtime(&t));
sprintf(sql, "update fruit set stockpile=%d ,sell_time='%s' where name='%s';", stockpile, tmpBuf, name);
ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
if (ret != SQLITE_OK) {
printf("sell_control filed:%s\n", sqlite3_errmsg(ppdb));
exit(1);
}
printf("sell_control %s success\n\n\n", name);
}
void price_change(void)
{
char name[10];
float price;
printf("input fruit name:");
scanf("%s", name);
getchar();
printf("input new price:");
scanf("%f", &price);
getchar();
sprintf(sql, "update fruit set price=%f where name='%s';", price, name);
ret = sqlite3_exec(ppdb, sql, NULL, NULL, NULL);
if (ret != SQLITE_OK) {
printf("price_change filed:%s\n", sqlite3_errmsg(ppdb));
exit(1);
}
printf("price_change %s success\n\n\n", name);
}