本文系统介绍了数据库基础知识与应用操作。主要内容包括:1)数据库分类与核心概念,重点解析了SQLite嵌入式数据库的特点;2)SQLite常用指令与基本操作,涵盖表创建、增删改查、排序等语法;3)数据库维护命令(导入导出)与可视化工具;4)C语言操作SQLite的编程接口,包括打开/关闭数据库和执行SQL语句的函数使用示例。全文以SQLite为例,详细说明了轻量级数据库的实际应用方法,适合开发者快速掌握数据库基础操作。


一、数据库

数据库是结构化的数据集合,由数据库管理系统统一存储、管理,数据以表、行、列的形式组织。

1.分类

分类 代表数据库
大型数据库 ORACLE
中型数据库 MYSQL、MSSQL(SQL Server)
小型数据库 SQLITE、DBII、powdb

2.核心名词解释

缩写 英文全称 中文含义 补充说明
DB Database 数据库 存储数据的仓库,是数据的集合
DBMS Database Management System 数据库管理系统 管理数据库的软件(如 MySQL、Oracle、SQLite)
MIS Management Information System 管理信息系统 利用数据库进行业务管理的系统(如企业 ERP、学生管理系统)
OA Office Automation 办公自动化 用于日常办公流程管理的系统(如审批、文档管理)
SQL Structured Query Language 结构化查询语言 操作关系型数据库的标准语言,核心操作:SELECT(查询)、UPDATE(更新)等

3.嵌入式数据库  SQLlite

1)核心特点
  1. 开源 & 开发语言:完全开源,使用 C 语言 开发,性能高效且可移植性强。
  2. 轻量级:核心代码约 1 万行,总大小在 10MB 以内,非常小巧。
  3. 无需安装:是绿色软件,解压即可使用,不依赖系统环境。
  4. 文件型数据库:整个数据库就是一个独立文件,可直接复制、移动,部署极其方便。
  5. 容量上限:单数据库文件最大可支持 2TB 的数据存储。
2)与其他数据库对比
特性 SQLite MySQL Oracle
部署方式 单文件,无需服务 需要独立服务进程 企业级服务集群
并发能力 适合低并发读写 高并发,支持多用户 极高并发,企业级
主要场景 嵌入式、本地存储 中小型网站、Web 应用 大型企业核心系统
学习难度 极低,上手快 中等 高,体系复杂

二、数据库的指令

指令格式 功能说明
.database 列出当前数据库及关联的系统文件
.tables 列出当前数据库中的所有表
.schema 表名 查看指定表的结构(建表语句)
.headers on 开启查询结果的列名显示(让输出更易读)
.exit / .quit / .q 退出 SQLite 控制台(三种写法效果相同)

三、数据库的基本操作

操作台用:“sqlite3 文件名.db”唤出;所有的sql语句必须以分号结尾;SQLite 不区分大小写

1.创建一个表

create table 表名(表字段1 类型, 表字段2 类型, ...);

eg: create teable user(id int,name char, age int);

注:以上表的表字段,支持以下数据类型:

数据类型 说明 适用场景 示例写法
INT 整数类型(自动适配大小) 年龄、ID、数量、序号 id INT
REAL 双精度浮点数(小数) 身高、体重、温度、普通小数 height REAL
DECIMAL 精确小数(可指定精度) 金额、价格、财务数据 price DECIMAL(10,2)
TEXT 字符型 存储长字符串、非英文字符串 mean TEXT
BOOLEAN 布尔值(存 1/0) 状态、开关、是否启用 is_vip BOOLEAN

2.删除一个表

drop table 表名;

eg: drop table user;

3.向表中增加数据

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

eg:insert into user (age) values (12); //指定表字段增加数据

     intsert into use values (12,"wang",11);   //整体表字段增加数据,字符串用单引号或者双引号括起来

4.查询一个表

select 列名 from 表名 条件;

eg:select * from user;  //查看所有表数据

     select id from user;  //查看id字段的数据

     select id,name from user where not age < 30;  //在where后加筛选条件,数字可以直接加条件

      //字符串有格式要求:where like '张三'  等效于 where = ’张三‘;

     //字符串还可以模糊查找:where like '张%';(通配符%,表示张后面可跟任意多字符;通配符_表示一个任意字符)。

5.对表数据排序

select * from 表名 by 表字段;  //默认升序排列,在表字段后加desc为降序排列

eg:select * from user order by id;

     select * from user oder by id desc;

     select * from user limit 3;//将表id限制在3以下

6.更新表数据

update 表名 set 表字段 = 新数据  where 表字段 = 数据;

eg:update user set id = 3 where age = 23;//将age=23的那一行的id列数据改为3

7.删除表中数据

delete from 表名 满足条件;

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

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

     delete from user where id =1 and name = "zhang";  //删除表中id = 1 且 name = 'zhang'的数据

     delete from user where id = 1 or id = 2;  //删除 user 表中,id 等于 1 或者 id 等于 2 的所

有数据

8.插入时间列

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

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

  •  datetime() 函数,datetime('now'):获取当前的 UTC 时间(世界协调时间),比北京时间晚 8 小时。
  • datetime('now', '+8 hours'):在 UTC 时间基础上增加 8 小时,得到北京时间,这是在国内开发时最常用的写法,8 和 hours之间必须要空格!

9.自动增长列

CREATE TABLE user3(id INTEGER PRIMARY KEY ASC,name char,age int,dt datetime);
insert into user3 values (NULL,'李四',23,datetime('now'));

  • PRIMARY  KEY 主键是表中能唯一标识一条记录的字段。主键的值不可重复,也不能为空,保证每条数据唯一可识别。主键用于精准定位数据,方便查询、修改、删除。主键查询比普通字段快很多,因为主键默认自带索引,像目录一样直接定位,比普通字段查询效率高很多。
  • 主键 id 设为 INTEGER PRIMARY KEY 时,插入写 NULL 就是让数据库自动生成自增编号,不用自己管。

四、数据库的维护命令

1.数据的导出:

sqlite3 xxx.db .dump > xxx.sql

//将数据库名称为xxx的数据库整体导出到脚本中,可以直接通过vim指令查看。导出的数据库不会实时同步数据库的修改,在数据库改动后要再次导出才能看得见改变

2.数据的导入:

sqlite3 xxx.db < xxx.sql

注意:数据库的导出和输入是在系统终端命令使用的

3.可视化工具安装:

sudo apt-get install sqlitebrowser

五、相关函数

数据库编程步骤:打开数据库、读写数据库(增、删、改、查),关闭数据库

1. 打开数据库:

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

功能:打开指定 path 路径 + 文件名称的数据库,并将打开的地址指向 db 变量的句柄。

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

           db 用于接收数据库句柄的二级指针

返回值:成功 0;失败 非 0;

2. 关闭数据库:

sqlite3_close (sqlite3 * db);

功能:关闭指定的数据库,释放资源。

参数:db 已经打开的数据库句柄

返回值:成功 0;失败 非 0;

3. 执行 sql 语句:

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

功能:在 db 数据库上执行 sql 语句,并将结果返回。

参数:db 要执行 sql 的数据库句柄sql 要执行的 sql 语句

          fun 查询操作的回调函数,用于接收结果

          arg 传递给回调函数的参数,无回调则填 NULL

          errmsg 用于接收执行过程中的错误信息

返回值:执行成功 0;失败 非 0;

示例:

#include <sqlite3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{
    //定义SQLite数据库句柄指针(相当于文件描述符),初始化为NULL
  sqlite3 *db = NULL;
  // 1.打开数据库,获得数据的句柄
  int ret = sqlite3_open("aaa.db", &db);
  //判断是否打开成功,SQLITE_OK等于0,sqlite3_open成功返回0
  if (SQLITE_OK != ret)
  {
    fprintf(stderr, "open db error %s\n", sqlite3_errstr(ret));
    sqlite3_close(db);
    return 1;
  }

  //定义错误信息指针,用于接收sqlite3_exec执行失败的错误描述
  char *errmsg = NULL;
  //存储需要执行的指令
  char sql_cmd[512] = "insert into test values(7,'shuaige')";
  // 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;
}

注意:

1.插入数据前,要清除数据字符串里的换行符,或者会格式错乱

2.要将单词和释义中的单引号修改为双引号因为单引号是SQL的边界符号,防止程序误判

3.执行sqlite3_exec()数据库会:写入数据、刷新到磁盘、确认写入成功、继续下一条。因此如果放在while(1)循环中程序最多插入5000多条,很容易被卡死

4.在循环中反复调用 `sqlite3_exec` 执行单条 `INSERT` 语句时,SQLite 默认会为每条插入操作自动开启并提交一次独立事务。每次事务提交都会触发磁盘同步I/O、数据持久化刷盘、日志写入等操作,导致大量磁盘随机I/O与系统开销。 随着插入数据量增大,磁盘I/O性能瓶颈凸显,程序出现严重卡顿、吞吐量急剧下降,通常仅能稳定插入数千条数据便无法继续执行。

解决方案1:显式事务控制: 在批量插入循环前执行 `BEGIN TRANSACTION` 开启显式事务,所有插入操作在内存中完成;循环后执行 `COMMIT` 提交事务,将所有数据**一次性批量刷入磁盘。

解决方案2:关闭同步模式:执行 `PRAGMA synchronous = OFF;` 关闭数据库强制同步写盘机制。数据库不再等待数据完全写入磁盘,依赖操作系统缓存异步落盘,进一步降低I/O等待耗时,提升插入速度。 

六、编程数据库的查询

当需要对数据库中的数据,进行查询的时候。

sqlite3_exec()中的回调函数(第三个参数)就不能为空了。

通过回调函数反馈查询到结果。回调函数是用来返回结果。

如果结果集有5条记录,那么回调函数会调用5次。如果结果集有0条记录,那么回调函数会调用0次。show函数中,result 参数每次都会执行对应的记录。如果有多条记录,数据库会分多次把数据传递给result指针。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sqlite3.h>
int flag = 0;

// arg sqlite3_exec 传递过来的参数
// col  结果集列数
// result查询到的结果
// title 标题
// 这个函数的的调用次数,由 结果集决定 。如果结果由10 条,函数就会调用10次。
// result 对指向对应一条记录。
int show(void *arg, int col, char **result, char **title)
{
    int i = 0;
    if (0 == flag)  //只需要输出一次
    {
        flag = 1;
        for (i = 0; i < col; i++)
        {
            printf("%s\t", title[i]);
        }
        printf("\n");
    }

    for (i = 0; i < col; i++)
    {
        printf("%s\t", result[i]);
    }
    printf("\n");
    // 一定加上,否则查询语句只会运行一次 return 0
    return 0;
}

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;
    char sql_cmd[512] = "select * from user;";
    // 2 执行sql语句(对数据库的读写)
    ret = sqlite3_exec(db, sql_cmd, show, 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;
}

综合练习:将字典文件插入到数据库中,并实现 在数据库中查询目标单词 并反馈给终端

#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("aaa.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 dict(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 dict");
    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("/home/linux/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 dict 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 dict 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;
}

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐