Linux系统编程(十)--- 数据库Sqlite3

一、数据库的介绍

数据库(Database,简称DB)是一种应用程序,主要用于对数据进行存储、管理和统计。它通常与服务器搭配使用,为各类信息系统提供数据支持。在数据库中,数据以表的形式组织,一张表由多个记录(行)组成,每条记录又包含多个字段(列)。

根据规模和性能,数据库可分为大型、中型和小型。常见的关系型数据库包括 Oracle、MySQL/MSSQL、SQLite、DB2、PostgreSQL 等。相关的名词概念有:DB(数据库)、DBMS(数据库管理系统)、MIS(管理信息系统)、OA(办公自动化)等。

对数据库的操作主要通过 SQL(结构化查询语言)完成。SQL 语句可细分为三类:

  • DDL(data defination language 建表):用于建表等结构定义;

  • DML(data modifiy):用于新增、修改、删除行(如 update);

  • DQL(data query language):用于查询(如 select)。

本篇文章用到的是SQLite3。它是一个开源、采用 C 语言开发的数据库,代码量约 1 万行,总大小在 10M 以内,无需安装(绿色软件),以文件形式存在且可移动,最大支持 2T 数据。更多信息可访问官网 www.sqlite.org,代码托管于 www.kernel.org

特点:
1、开源 C语言开发
2、代码量少 1万行左右,总大小10M以内
3、绿色软件无需安装
4、文件型数据库,可以移动。
5、数据容量最大 2T

二、数据库的相关指令

1.安装和启动:

Linux终端下输入:

安装:

sudo apt-get install sqlite3

sudo apt-get install libsqlite3-dev

启动:

sqlite3 xxx.db

2.数据库的指令:

Sqlite3下输入:(注意数据库是不区分大小写的)

.database 列出当前库和系统中那个文件在关联
.tables 列出当期数据库中的所有表
.schema xxx 列出当前指定的xxx表结构

.headers on 查询数据的时候显示列名

.exit 或者.quit或者**.q 退出数据库控制台**

创建表:

create table 表名 (表字段 类型,表字段 类型,。。。。);

create table user(id int ,name char,age int);

CREATE TABLE IF NOT EXISTS user (id INTEGER PRIMARY KEY,word char,mean char);//若有表就创建,没有就跳过

删除一个表:

drop table 表名;

drop table user;

插入数据:

insert into 表名 (字段名称 ) values (值名称);

insert into user (id,age) values (1,10);

insert into user values(3,"wang",11);

insert into user (age) values ( 12);

查询表中的数据:

数据库的导入导出

select 列名 from 表名 条件;
select * from user ; //所有 的列

select id from user;

select id,name from user where not age <30

(可以使用&& or and增加条件)

通配符:

% 任意多个任意字符
_ 1个任意字符

select * from user where name like '张_ ' and id <5;

select * from user where name like '张%' and id <5;

修改表中数据:

update 表名 set 表字段 = 值 满足条件:

eg: update user set id = 1 where name = 'li';

update user set id = 1 where name = "li" and passwd = "123";

update user set id = 2 where name = "li" or name = "zhao";

删除表中数据:

delete from 表名 满足条件:

eg:delete from user ; ///删除表中所有数据

delete from user where id = 1; ///删除id=1 的数据;

delete from user where id =1 and name = "zhang";

delete from user where id = 1 or id = 2;

插入时间列 int int:

CREATE TABLE user1(id int,name char,age int,dt datetime);

insert into user1 values (2,'张三',23,datetime('now','+8 hours'));

自动增长列:

CREATE TABLE user3(id INTEGER PRIMARY KEY ,name char,age int,dt datetime);

insert into user3 values (NULL,'李四',23,datetime('now'));

PRIMARY KEY 主键 ,唯一性, 如果使用主键作为搜索条件,比普通字段块。

三、数据库的导入导出

linux终端中输入:

1、数据的导出
sqlite3 xxx.db .dump > xxx.sql

将数据库名称为xxx的数据库整体导出到脚本中。

2、数据的导入
sqlite3 xxx.db < xxx.sql

四、数据库的编程步骤:

打开数据库 ==》读写数据库(增,删,改,查) ==》关闭数据库

相关函数:

打开数据库:

int sqlite3_open(char * path,sqlite3 ** db);

功能:打开指定path路径+文件名称的数据库,并将

打开的地址指向db变量的句柄。

参数:path 要打开的数据库路径+名称

db 要打开的数据库地址指针

返回值:成功 0

失败 -1;

关闭数据库:

int sqlite3_close(sqlite3 *db);

功能:关闭指定的数据库

参数:要关闭的数据库地址

返回值:成功 0

失败 -1;

执行sql 语句

int sqlite3_exec(sqlite3 *db,char *sql,callback fun,void * arg,char **errmsg);

功能:在db数据库上执行sql语句。

并将结果返回。

参数:db 要执行sql的数据库

sql 要执行的非查询sql语句。

fun 如果sql语句是查询操作,需要 这个函数,则该回调函数用来回收查询的结果。

arg 回调函数的参数,如果没有回调函数则该参数为NULL;
errmsg 执行过程中的错误信息。//通常定义一个char *errmsg = NULL;传入函数

返回值:执行成功 0(SQLITE_OK)

失败 非0 ;

返回值的宏:

|-----------------|---|----------|----------------|
| SQLITE_ERROR | 1 | 通用错误 | SQL 语法错误、表不存在等 |
| SQLITE_BUSY | 5 | 数据库文件被锁定 | 其他进程正在写入数据库 |
| SQLITE_LOCKED | 6 | 表被锁定 | 同一连接内的并发访问冲突 |

编程数据库的查询:

当需要对数据库中的数据,进行查询的时候。
sqlite3_exec() 中的回调函数(第三个参数)就不能为空了。
通过回调函数反馈查询到结果。 回调函数是用来返回结果。
如果结果集有5条记录,那么回调函数会调用5次。如果结果集有0条记录,那么回调函数会调用0次。(会根据结果集的记录来调用函数)

show函数中,result 参数每次都会执行对应的记录。

如果有多条记录,数据库会分多次把数据传递给result 指针。

注意:show函数中必须返回0,如果不返回0,sqlite3_exec()会返回错误信息

示例1:给已有的数据库插入数据

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sqlite3.h>

int main(int argc, char **argv)
{
    sqlite3 *db = NULL;
    // 1 打开数据库,获得数据的句柄(相当于linux 中的文件描述符)
    int ret = sqlite3_open("aaa.db", &db);
    if (SQLITE_OK != ret)
    {
        fprintf(stderr, "open db error  %s\n", sqlite3_errstr(ret));
        sqlite3_close(db);
        return 1;
    }
    char *errmsg = NULL;
    //需要执行的sql语句, 不要执行查询语句(select),
    char sql_cmd[512] = "insert into user values(7,'帅哥',20)";
    // 2 执行sql语句(对数据库的读写)
    ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
    if (SQLITE_OK != ret)
    {
        fprintf(stderr, "sqlite3_exec  sql_cmd:[%s] ,%s\n", sql_cmd, errmsg);
        sqlite3_free(errmsg);
        sqlite3_close(db);
        return 1;
    }
    // 3. 关闭数据 释放资源
    sqlite3_close(db);

    return 0;
}

示例2:创建新的数据库插入数据

复制代码
#include <errno.h>
#include <fcntl.h>
#include <sqlite3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char **argv)
{
  // 1.创建db文件,没有就直接到第二步
  int dbfd = open("dict.db", O_RDONLY | O_TRUNC);
  if (-1 == dbfd)  //出错
  {
    if (errno == ENOENT)  //错误原因:文件不存在
    {
      // fork,创建db文件
      pid_t pid;
      pid = fork();
      if (pid > 0)  //父进程
      {
      }
      else if (0 == pid)  //子进程创建db并退出
      {
        if (execlp("sqlite3", "sqlite3", "dict.db", NULL) < 0)
        {
          perror("execl fail");
        }
        exit(1);  //创建好后结束进程
      }
      else  // fork失败
      {
        perror("fork");
        return -1;
      }
    }
  }
  else  // db存在
  {
    close(dbfd);  //文件存在,关闭描述符
  }

  // 第二步 打开数据库,获得数据的句柄(相当于linux 中的文件描述符)
  sqlite3 *db = NULL;
  int ret = sqlite3_open("dict.db", &db);
  if (SQLITE_OK != ret)
  {
    fprintf(stderr, "open db error  %s\n", sqlite3_errstr(ret));
    sqlite3_close(db);
    return 1;
  }
  //第三步创建table
  char sql_cmd[512] = {0};
  char *errmsg = NULL;
  strcpy(sql_cmd, "CREATE TABLE IF NOT EXISTS user (id INTEGER PRIMARY KEY,word char,mean char)");
  ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
  if (SQLITE_OK != ret)
  {
    fprintf(stderr, "sqlite3_exec  sql_cmd:[%s] ,%s\n", sql_cmd, errmsg);
    sqlite3_free(errmsg);
    sqlite3_close(db);
    return 1;
  }

  //第四步把dict.txt插入数据库
  FILE *fp = fopen("dict.txt", "r");  //打开文件
  if (fp == NULL)
  {
    perror("fopen fail");
    return -1;
  }

  while (1)  //循环 //获取dict.txt内容
  {
    char buf[1024] = {0};
    char *tmp[2] = {0};
    if (fgets(buf, sizeof(buf), fp) == NULL)  //读取完dict.txt
    {
      printf("插入完成\n");
      break;
    }
    tmp[0] = strtok(buf, " ");
    tmp[1] = strtok(NULL, "\n");

    sprintf(sql_cmd, "insert into user values(NULL,\"%s\",\"%s\")", tmp[0], tmp[1]);
    // 2 执行sql语句(对数据库的读写)
    ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
    if (SQLITE_OK != ret)
    {
      fprintf(stderr, "sqlite3_exec  sql_cmd:[%s] ,%s\n", sql_cmd, errmsg);
      sqlite3_free(errmsg);
      sqlite3_close(db);
      return 1;
    }
  }
  // 3. 关闭数据 释放资源
  fclose(fp);
  sqlite3_close(db);

  return 0;
}

五、SQlite事务(Transaction)

事务(Transaction)是以逻辑顺序完成的工作单位或序列,可以是由用户手动操作完成,也可以是由某种数据库程序自动完成

原子性(Atomicity) :确保工作单位内的所有操作都成功完成,否则,事务会在出现故障时终止,之前的操作也会回滚到以前的状态。
一致性(Consistency) :确保数据库在成功提交的事务上正确地改变状态。
隔离性(Isolation) :使事务操作相互独立和透明。
持久性(Durability):确保已提交事务的结果或效果在系统发生故障的情况下仍然存在

命令:

BEGIN TRANSACTION:开始事务处理。

COMMIT:保存更改,或者可以使用:END TRANSACTION 命令。

ROLLBACK:回滚所做的更改。

在上面的示例2中,有批量操作。 如果没有开启事务。 每条插入语句,都会先开启事务,插入结束后在关闭事务。会导致效率下降,插入数据速度非常慢。

解决方案 :在批量插入前开启事务,插入结束后提交事务。 提高插入的效率。

示例2:(更改后:加入了查询功能和解决了插入过慢的问题)

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sqlite3.h>

int find_word(void*arg,int col,char**result,char**title)
{
    *(int*)arg = 1;
    printf("%s:%s\n",result[1],result[2]);
    return 0;
}

int main(int argc, char **argv)
{
    sqlite3 *db = NULL;
    // 1 打开数据库,获得数据的句柄(相当于linux 中的文件描述符)
    int ret = sqlite3_open("dict.db", &db);
    if (SQLITE_OK != ret)
    {
        fprintf(stderr, "open db error  %s\n", sqlite3_errstr(ret));
        sqlite3_close(db);
        return 1;
    }
    char sql_cmd[1024] = {0};
    char *errmsg = NULL;

    // 创建表
    bzero(sql_cmd, sizeof(sql_cmd));
    strcpy(sql_cmd,
           "create table IF NOT EXISTS user(id INTEGER PRIMARY KEY ASC,word "
           "char,mean text);");
    // 2 执行sql语句(对数据库的读写)
    ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
    if (SQLITE_OK != ret)
    {
        fprintf(stderr, "sqlite3_exec  sql_cmd:[%s] ,%s\n", sql_cmd, errmsg);
        sqlite3_free(errmsg);
        sqlite3_close(db);
        return 1;
    }

    bzero(sql_cmd, sizeof(sql_cmd));  // 清空表
    strcpy(sql_cmd, "delete from user");
    ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
    if (SQLITE_OK != ret)
    {
        fprintf(stderr, "sqlite3_exec  sql_cmd:[%s] ,%s\n", sql_cmd, errmsg);
        sqlite3_free(errmsg);
        sqlite3_close(db);
        return 1;
    }

    bzero(sql_cmd, sizeof(sql_cmd));  // 为了提高效率,批量操作 ,开启事务
    strcpy(sql_cmd, "BEGIN TRANSACTION;");
    ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
    if (SQLITE_OK != ret)
    {
        fprintf(stderr, "sqlite3_exec  sql_cmd:[%s] ,%s\n", sql_cmd, errmsg);
        sqlite3_free(errmsg);
        sqlite3_close(db);
        return 1;
    }

    FILE *fp = fopen("dict.txt", "r");
    if (NULL == fp)
    {
        perror("fopen");
        return 1;
    }
    int i = 1;
    while (1)
    {
        char line_buf[1024] = {0};
        if (NULL == fgets(line_buf, sizeof(line_buf), fp))
        {
            break;
        }

        char *word = strtok(line_buf, " ");
        char *mean = strtok(NULL, "\r");

        bzero(sql_cmd, sizeof(sql_cmd));  
        sprintf(sql_cmd, "insert into user values(NULL,\"%s\",\"%s\");", word,
                mean);
        ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
        if (SQLITE_OK != ret)
        {
            fprintf(stderr, "sqlite3_exec  sql_cmd:[%s] ,%s\n", sql_cmd,
                    errmsg);
            sqlite3_free(errmsg);
            sqlite3_close(db);
            return 1;
        }
        // printf("num:%d\n", i++);
    }

    bzero(sql_cmd, sizeof(sql_cmd));  // 提交事务 ,相当于保存文件
    strcpy(sql_cmd, "COMMIT;");
    ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
    if (SQLITE_OK != ret)
    {
        fprintf(stderr, "sqlite3_exec  sql_cmd:[%s] ,%s\n", sql_cmd, errmsg);
        sqlite3_free(errmsg);
        sqlite3_close(db);
        return 1;
    }

    while (1)
    {
        printf("input word:");
        char word[50] = {0};
        fgets(word, sizeof(word), stdin);
        word[strlen(word) - 1] = '\0';
        if(0 == strcmp("#quit",word))
        {
            break;
        }
        int flag = 0 ;
        bzero(sql_cmd, sizeof(sql_cmd));  // 清空表
        sprintf(sql_cmd,"select * from user where word like \"%s\";",word);
        ret = sqlite3_exec(db, sql_cmd, find_word,&flag , &errmsg);
        if (SQLITE_OK != ret)
        {
            fprintf(stderr, "sqlite3_exec  sql_cmd:[%s] ,%s\n", sql_cmd,
                    errmsg);
            sqlite3_free(errmsg);
            sqlite3_close(db);
            return 1;
        }
        if(0 == flag)
        {
           printf("can't find\n"); 
        }
    }
    // 3. 关闭数据 释放资源
    sqlite3_close(db);

    return 0;
}

六、总结

假如要写入sql_cmd命令的字符串里面有单引号了,这时候写成:

sprintf(sql_cmd, "insert into user values(NULL,'%s','%s')", tmp[0], tmp[1]);

tmp[0]或tmp[1]里面有单引号的字符串

这样执行命令肯定会报错,所以可以写成这样:

sprintf(sql_cmd, "insert into user values(NULL,\"%s\",\"%s\")", tmp[0], tmp[1]);

相关推荐
222you2 小时前
MongoDB的安装和整合SpringBoot
数据库·spring boot·mongodb
秦jh_2 小时前
【Redis】通用命令、string类型
数据库·redis·缓存
不懒不懒2 小时前
【苏宁易购商品评价文本分析实战:从自动化爬取到分词清洗全流程】
运维·数据库·自动化
Predestination王瀞潞2 小时前
动态 SQL 的核心标签及使用细节
数据库·sql
_OP_CHEN2 小时前
【MySQL数据库基础】(三)MySQL 库的核心操作全解析:创建、修改、备份一条龙搞定
linux·数据库·sql·mysql·c/c++·mysql操作·企业级组件
数据知道2 小时前
MongoDB心跳检测与故障转移:自动主从切换的全过程解析
数据库·mongodb·wpf
古城小栈2 小时前
MongoDB go快速操控
数据库·mongodb·golang
小年糕是糕手2 小时前
【35天从0开始备战蓝桥杯 -- Day2】
开发语言·jvm·数据库·c++·程序人生·考研·蓝桥杯
数据知道2 小时前
MongoDB:如何构建“数据回收站“,防止人为误删数据(延迟节点)
数据库·mongodb