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')", tmp0, tmp1);

tmp0或tmp1里面有单引号的字符串

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

sprintf(sql_cmd, "insert into user values(NULL,\"%s\",\"%s\")", tmp0, tmp1);

相关推荐
z123456789865 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
程序猿DD5 小时前
一个 API Key,统一调用大模型、生图和联网搜索
数据库·网关
TlSfoward6 小时前
抓包代理链路下的 TLS 指纹变化分析 TLSFOWARD抓包工具
数据库·爬虫·网络协议·搜索引擎·php
数智化管理手记7 小时前
账龄分析手工统计易遗漏?自动账龄分析工具怎么搭建
大数据·网络·数据库·人工智能·数据挖掘
LabVIEW开发7 小时前
NI Package Manager安装LabVIEW时InternalServerError错误
网络·数据库·labview·labview知识·labview功能·labview程序
SelectDB7 小时前
PostgreSQL 实时同步到 Apache Doris:一站式 CDC 解决方案
数据库
Database_Cool_8 小时前
AI Agent 长期记忆怎么存?阿里云 PolarDB-X 对接 Mem0 记忆框架实践
数据库·阿里云
会编程的土豆8 小时前
数据库范式
数据库·oracle
L1624768 小时前
MySQL 全环境生产快速安装 + 完整配置手册(整合国产银河麒麟适配 + 主从补充版)
数据库·mysql
yio_yin8 小时前
Spring事务管理(数据一致性)
数据库·sql·spring