• 使用香橙派学习 嵌入式数据库---SQLite


    嵌入式数据库简介:SQLite & MySQL数据库

    基于嵌入式的数据库主要有:SQLite,Firebird,Berkeley DB,eXtremeDB等

    • Firebird 是关系型数据库,功能强大,支持存储过程,SQL兼容等
    • SQLite 关系型数据库,体积小,支持ACID事务
    • Berkeley DB 并没有数据库服务器的概念,他的程序直接链接到应用程序中
    • eXtremeDB 是内存数据库,运行效率高

    SQLite数据库

    轻量化,易用的嵌入式数据库,用于设备端的数据管理,可以理解成单点的数据库。(传统服务器型数据库用于管理多端设备,更加复杂SQLite是一个无服务器的数据库,是自包含的。这也称为嵌入式数据库,这意味着数据库引擎作 为应用程序的一部分运行。

    MySQL数据库

    MySQL需要运行服务器,MySQL将需要客户端和服务器架构通过网络进行交互

    在香橙派上安装SQLite3数据库

    执行以下命令:

    sudo apt-get -y install sqlite

    安装成功后,执行“sqlite” 即可进入数据库命令行:

    但是此时的版本是2.8.17,一般sqlite3的使用场景更多,所以我想升级以下版本,运行“sudo apt-get -y install sqlite3

    但是报错,于是只能到官网去下载:SQLite Download Page 

    进入官网后,选择Source Code下的第二个:

    将下载的文件上传到香橙派:

     

    然后,依次执行以下的命令:

    1. tar xvf sqlite-autoconf-3430100.tar.gz //解压
    2. cd sqlite-autoconf-3430100 //进入文件夹
    3. ./configure --prefix=/usr/local //配置安装路径在/usr/local
    4. make //编译,这一步比较久,大概10分钟
    5. sudo make install //安装

    成功安装后,输入“sqlite3”:

    可见,此时版本成功变成了3.43.1,并成功连入了数据库!

    可以输入“ .quit ”退出:

    SQLite的基本操作

    在工作目录“/home/orangepi/”下创建“mjm_sqlite”文件夹,将数据库文件存放在这里:

    数据库的创建/打开

    方法1:
    1. 输入“sqlite3” 进入数据库
    2. 输入“.open test.db
    3. 输入“.quit” 退出

    方法2:
    1. 输入“sqlite3 test.db” 在命令运行当前窗口创建数据库test.db
    2. 输入“.databases” 列出当前打开的数据库
    3. 输入“.quit” 退出

    表格的创建/打开

    数据库表格的创建和结构体很类似,在实际中也经常将两者进行转换:

    create table stu(id Integer,name char,score Integer); //在这里“int”应该写成“Integer”,且写在变量名的后方

     

    上图演示了:打开刚刚创建的test.db数据库,并在其中添加一张名为stu的表格,表格有三个字段:整型的id和score,char型的name

    和“.databases”类似,输入“.tables”可以查看当前打开的表格:

    数据库创建和表格创建的区别 

    • 执行“.open A.db”时,如果一个数据库A.db不存在则会创建并打开;若该数据库已经存在就会打开;且 “.open A.db”不是SQL命令,对应的C接口是sqlite3_open( )函数
    • 执行“create table AA(....)”时,如果表AA不存在则会创建并打开;若表格已经存在则会报错;且 “create table AA(....)”是SQL命令,对应的C接口是sqlite3_exec( )函数

    插入一条记录

    1. insert into stu values(18130106,'huang',99); //这个版本的sqlite只能使用'huang'而不能使用"huang"!!!
    2. //也可以此一次只插入部分的字段内容,比如只插入name和score,不插入id
    3. insert into stu(name,score) values('huanggang',77); //插入部分字段内容

     

    上图分别演示了:将一个名为huang的同学的id,name,score 插入stu表格;将一个名为huanggang的同学的name,score部分插入stu表格

    查看数据库的记录

    1. select * from stu; //查询所有字段的结果
    2. select name,score from stu; //查询数据库中部分字段的内容

     

    上图分别演示了:查看stu表格的所有记录;查看stu表格下name和score的全部记录

    删除一条记录

    delete from stu where id = 18130106;
    

     

    上图演示了:删除了id为18130106的记录,即名为“huang”的同学的记录被删除了,所以只剩下了“huanggang”同学

    更改一条记录

    update stu set name = 'majia' where score = 77;
    

    上图演示了:将分数为77的同学的名字修改为“majia”

    增加一列

    alter table stu add column sex char;
    

     

    上图演示了:为stu表格新增一列char型的sex的字段,然后将分数为77的同学的id设为12133,sex设为male

    删除一张表

    drop table stu;

     

    上图演示了:删除stu表格

    错误处理方式

    当输入了无法识别的指令,数据库会进入“...>”的状态:

    此时输入“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接口返回的错误代码
    • 错误代码一览:

    例程sql_demo1.c:
    1. #include
    2. #include
    3. #include
    4. int main(int argc, char** argv)
    5. {
    6. sqlite3 *db;
    7. char *zErrMsg = 0;
    8. int ret;
    9. if(argc < 2){
    10. printf("Usage: %s xxx.db\n",argv[0]);
    11. return -1;
    12. }
    13. ret = sqlite3_open(argv[1], &db);
    14. if(ret != SQLITE_OK){
    15. printf("Can't open database: %s\n", sqlite3_errmsg(db));
    16. exit(0);
    17. }else{
    18. printf("Opened database successfully\n");
    19. }
    20. sqlite3_close(db);
    21. printf("close database\n");
    22. }
    实现效果: 

    使用gcc编译会报错,需要链库,所以可以和之前一样写一个shell脚本

    buildsql.sh:

    gcc $1 -lsqlite3

    然后输入“chmod +x buildsql.sh”赋予权限就可以了 

    如果test.db本来就存在,那么“./a.out test.db” 也会成功运行,只不过从“创建test.db并关闭”变成了“打开test.db并关闭”

    sqlite3_exec函数的引入 & SELECT操作

    在学习接下来的操作之前,需要学习这个sqlite3_exec( )和其对应的callback( )函数:

    sqlite3_exec(sqlite3*, const char *sql, sqlite_callback, void *data, char **errmsg)
    
    • 该指令提供了一个执行 SQL 命令的快捷方式,SQL 命令由 sql 参数提供,可以由多个 SQL 命令组成,此处命令的不同就决定了实现功能的不同,所以之后创建表;插入数据;SELECT;UPDATE;DELETE本质上都是调用这个函数,只不过这个参数的值和callback的处理可能不同
    • 第一个参数 sqlite3 是打开的数据库对象
    • sqlite_callback 是一个回调,data 作为回调函数的第一个参数
    • errmsg 将被返回用来获取程序生成的任何错误
    int callback(void *arg, int column_size, char *column_value[], char *column_name[])
    • 第一个参数void *arg 是sqlite3_exec函数的第四个参数data
    • column_size:数据库的字段 数
    • column_value[ ]:列的值
    • column_name:字段名字 

    为了验证这个函数,可以先写一个demo执行一句简单的sql指令:

    例程sql_demo2.c: (实现“select * from stu;”的sql指令
    1. #include
    2. #include
    3. #include
    4. int callback(void *arg, int column_size, char *column_value[], char *column_name[])
    5. {
    6. int i;
    7. printf("arg=%s\n",(char *)arg);
    8. for(i=0;i
    9. printf("%s = %s\n", column_name[i], column_value[i]);
    10. }
    11. printf("=======================\n");
    12. return 0;//必须返回0,这样数据库中有多少条数据,这个回调函数就会被调用多少次
    13. }
    14. int main(int argc, char** argv)
    15. {
    16. sqlite3 *db;
    17. char *zErrMsg = 0;
    18. int ret;
    19. if(argc < 2){
    20. printf("Usage: %s xxx.db\n",argv[0]);
    21. return -1;
    22. }
    23. /*open database*/
    24. ret = sqlite3_open(argv[1], &db);
    25. if(ret != SQLITE_OK){
    26. printf("Can't open database: %s\n", sqlite3_errmsg(db));
    27. exit(0);
    28. }else{
    29. printf("Opened database successfully\n");
    30. }
    31. /* execute sql statement */
    32. ret = sqlite3_exec(db, "select * from stu;", callback, 0, &zErrMsg);
    33. /* close database */
    34. sqlite3_close(db);
    35. printf("close database\n");
    36. return 0;
    37. }

    为什么 &zErrMsg不会报段错误?因为如果发生错误,函数底层应该会为zErrMsg分配空间然后写入错误信息。

    实现效果:

     

    创建表 & 插入数据

    小插曲1:

    “ .tables ” 不属于SQL语句,所以这句命令写在sqlite3_exec( )中会出错!

    判断表格是否存在只需要在sqlite3_exec( )中写创建表格的SQL语句,然后通过返回值来判断表格是否存在即可!

    小插曲2:

    sqlite3_exec( )函数中的提到的callback函数是不一定会执行的,比如SQL指令是创建表格或插入数据,那么callback就不会执行(对应在sql环境下输入创建表格或插入数据的指令也不会输出任何东西),但如果执行SELECT指令的话callback函数就会执行(对应在sql环境下输入SELECT语句会显示对应的结果),所以可以粗略概括为:只有在SQL环境下会有反馈输出的SQL指令出现在sqlite3_exec( )函数中,callback才会被调用

    小插曲3:

    • PRIMARY KEY是主键值的意思,主键值必须是能够区分每一行数据的值,比如ID是主键值,那每一组数据的ID都应该是不同的。
    • 而如果一个字段被指定了“NOT NULL”,就代表这个字段不能为空

    小插曲4:

    再次提醒,在SQL命令中,字符串要使用'XXX'而不是"XXX"!! 

    例程sql_demo3.c:

    创建一个名为“company”的表格

    1. #include
    2. #include
    3. #include
    4. int callback(void *arg, int column_size, char *column_value[], char *column_name[])
    5. {
    6. int i;
    7. printf("arg=%s\n",(char *)arg);
    8. for(i=0;i
    9. printf("%s = %s\n", column_name[i], column_value[i]);
    10. }
    11. printf("=======================\n");
    12. return 0;//必须返回0,这样数据库中有多少条数据,这个回调函数就会被调用多少次
    13. }
    14. int main(int argc, char** argv)
    15. {
    16. sqlite3 *db;
    17. char *zErrMsg = 0;
    18. int ret;
    19. char *sql;
    20. if(argc < 2){
    21. printf("Usage: %s xxx.db\n",argv[0]);
    22. return -1;
    23. }
    24. /*open database*/
    25. ret = sqlite3_open(argv[1], &db);
    26. if(ret != SQLITE_OK){
    27. printf("Can't open database: %s\n", sqlite3_errmsg(db));
    28. exit(0);
    29. }else{
    30. printf("Opened database successfully\n");
    31. }
    32. /* execute sql statement -- create table */
    33. sql = "CREATE TABLE COMPANY(" \
    34. "ID INT PRIMARY KEY NOT NULL," \
    35. "NAME CHAR(30) NOT NULL," \
    36. "AGE INT NOT NULL," \
    37. "ADDRESS CHAR(50)," \
    38. "SALARY REAL );";
    39. ret = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
    40. if(ret != SQLITE_OK){
    41. printf("Can't create table: %s\n", sqlite3_errmsg(db));
    42. exit(0);
    43. }else{
    44. printf("Table create successfully\n");
    45. }
    46. /* execute sql statement -- insert data */
    47. sql = "insert into company values(1813,'company.1',3,'aaa street No.293',37732);";
    48. ret = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
    49. if(ret != SQLITE_OK){
    50. printf("Can't insert data: %s\n", sqlite3_errmsg(db));
    51. exit(0);
    52. }else{
    53. printf("Inset data successfully\n");
    54. }
    55. /* execute sql statement -- show content */
    56. ret = sqlite3_exec(db, "select * from company;", callback, 0, &zErrMsg);
    57. if(ret != SQLITE_OK){
    58. printf("Can't show content: %s\n", sqlite3_errmsg(db));
    59. exit(0);
    60. }
    61. /* close database */
    62. sqlite3_close(db);
    63. printf("close database\n");
    64. return 0;
    65. }
    实现效果: 

    我重新创建了一个叫“companydb.db”的数据库来存放这个表格

    可见,表格和数据分别创建和插入成功 ,并且callback只有最后一次SELECT命令时才被调用

    再尝试进入SQL环境验证:

     

    并且,如果再次执行命令就会报错,并提示表格已经存在

    UPDATE操作 & DELETE操作

    例程sql_demo4.c:

    对于刚刚创建的company表格,更新它的地址,然后删除这条记录

    1. #include
    2. #include
    3. #include
    4. int callback(void *arg, int column_size, char *column_value[], char *column_name[])
    5. {
    6. int i;
    7. printf("arg=%s\n",(char *)arg);
    8. for(i=0;i
    9. printf("%s = %s\n", column_name[i], column_value[i]);
    10. }
    11. printf("=======================\n");
    12. return 0;//必须返回0,这样数据库中有多少条数据,这个回调函数就会被调用多少次
    13. }
    14. int main(int argc, char** argv)
    15. {
    16. sqlite3 *db;
    17. char *zErrMsg = 0;
    18. int ret;
    19. char *sql;
    20. if(argc < 2){
    21. printf("Usage: %s xxx.db\n",argv[0]);
    22. return -1;
    23. }
    24. /*open database*/
    25. ret = sqlite3_open(argv[1], &db);
    26. if(ret != SQLITE_OK){
    27. printf("Can't open database: %s\n", sqlite3_errmsg(db));
    28. exit(0);
    29. }else{
    30. printf("Opened database successfully\n");
    31. }
    32. /* execute sql statement -- create table */
    33. sql = "CREATE TABLE COMPANY(" \
    34. "ID INT PRIMARY KEY NOT NULL," \
    35. "NAME CHAR(30) NOT NULL," \
    36. "AGE INT NOT NULL," \
    37. "ADDRESS CHAR(50)," \
    38. "SALARY REAL );";
    39. ret = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
    40. if(ret != SQLITE_OK){
    41. printf("Can't create table: %s\n", sqlite3_errmsg(db));
    42. //exit(0); 这里将exit注释掉的原因是如果表格已经存在,不需要退出,而是继续执行即可
    43. }else{
    44. printf("Table create successfully\n");
    45. }
    46. /* execute sql statement -- show content */
    47. ret = sqlite3_exec(db, "select * from company;", callback, "before update", &zErrMsg);
    48. if(ret != SQLITE_OK){
    49. printf("Can't show content1: %s\n", sqlite3_errmsg(db));
    50. exit(0);
    51. }
    52. /* execute sql statement -- update data */
    53. sql = "update company set address = 'ccc street No.666' where id = 1813;";
    54. ret = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
    55. if(ret != SQLITE_OK){
    56. printf("Can't update data: %s\n", sqlite3_errmsg(db));
    57. exit(0);
    58. }else{
    59. printf("Update data successfully\n");
    60. }
    61. /* execute sql statement -- show content */
    62. ret = sqlite3_exec(db, "select * from company;", callback, "after update", &zErrMsg);
    63. if(ret != SQLITE_OK){
    64. printf("Can't show content2: %s\n", sqlite3_errmsg(db));
    65. exit(0);
    66. }
    67. /* execute sql statement -- delete data */
    68. sql = "delete from company where id = 1813;";
    69. ret = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
    70. if(ret != SQLITE_OK){
    71. printf("Can't delete data: %s\n", sqlite3_errmsg(db));
    72. exit(0);
    73. }else{
    74. printf("Delete data successfully\n");
    75. }
    76. /* execute sql statement -- show content */
    77. ret = sqlite3_exec(db, "select * from company;", callback, "after delete", &zErrMsg);
    78. if(ret != SQLITE_OK){
    79. printf("Can't show content3: %s\n", sqlite3_errmsg(db));
    80. exit(0);
    81. }
    82. /* close database */
    83. sqlite3_close(db);
    84. printf("close database\n");
    85. return 0;
    86. }
    实现效果: 

    可见,由于表格已经存在,所以创建表格的命令出错了,不过由于我的修改,代码会继续运行而不是直接exit更新数据后,地址的确改变了而删除数据后,由于本来就只有一条记录,所以此时company表没有数据,所以此时哪怕调用了“select * from company;” sqlite3_exec函数也不会调用callback来打印信息!

  • 相关阅读:
    20篇NeurIPS论文精选:语言大模型的六大趋势
    05Java数据类型/标识符/键盘录入
    vivado HW_DEVICE
    shell语法(一)
    20220809NOI模拟赛
    Vue实现角色权限动态路由详细教程,在vue-admin-template基础上修改,附免费完整项目代码
    【RabbitMQ】——主题模式(Topics)
    linux7上powerpath卸载
    【1】MongoDB的安装以及连接
    [面试常考]父元素高度坍塌的原因及其解决方法
  • 原文地址:https://blog.csdn.net/mjmmm/article/details/133112657